React
Where the code goes
Save the generated component as ContactForm.tsx (it imports nothing but React) and render <ContactForm /> where the form belongs. Generate it with formto_generate_form_code (framework react) or from the dashboard.
The origin to allow
Allow your app's hostname as an allowed origin, plus localhost while you develop.
Thank-you handling
The component keeps the thank-you state itself: after a successful post it clears the form and shows the message. Replace the message with your own component if you like.
File uploads
File inputs work as is: the component posts FormData, so a file field in the definition uploads with the response.
The code
Generated from the form’s fields (a React component). Replace YOUR_FORM_ID with your form’s public id, then restyle it; keep the field names and the hidden _gotcha honeypot.
import { useState, type FormEvent } from 'react'
const ACTION = "https://api.formto.io/f/YOUR_FORM_ID"
export default function ContactForm() {
const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle')
const [error, setError] = useState('')
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
const form = event.currentTarget
setStatus('sending')
try {
const res = await fetch(ACTION, { method: 'POST', body: new FormData(form), headers: { Accept: 'application/json' } })
const body = (await res.json()) as { success?: boolean; error?: string }
if (!res.ok || !body.success) throw new Error(body.error || 'Something went wrong. Please try again.')
form.reset()
setStatus('sent')
} catch (err) {
setError(err instanceof Error ? err.message : 'Could not send. Please try again.')
setStatus('error')
}
}
return (
<form action={ACTION} method="post" encType="application/x-www-form-urlencoded" onSubmit={onSubmit}>
<h2>{"Contact"}</h2>
<p>
<label htmlFor="formto-name">{"Name"}</label>
<input type="text" id="formto-name" name="name" required />
</p>
<p>
<label htmlFor="formto-email">{"Email"}</label>
<input type="email" id="formto-email" name="email" autoComplete={"email"} required />
</p>
<p>
<label htmlFor="formto-message">{"Message"}</label>
<textarea id="formto-message" name="message" rows={5} required />
</p>
{/* Spam trap: people never see or fill this field. */}
<input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" aria-hidden="true" style={{ position: 'absolute', left: '-10000px' }} />
<button type="submit" disabled={status === 'sending'}>Send</button>
<p role="status" aria-live="polite">{status === 'sent' ? 'Thanks, we got it.' : status === 'error' ? error : ''}</p>
</form>
)
}
Other platforms: Plain HTML, Next.js, Astro, WordPress, Squarespace, Webflow, Wix, Framer, Shopify.