React Hook Form gives you structured values and validation hooks. You can submit those values directly to Formbase as JSON, or switch to FormData when you need file uploads.
These examples use client-side fetch. If you prefer a JSON response from Formbase, submit from a server action or API route.
Create your form
Register fields as usual with useForm.
Submit to Formbase
JSON submission
File uploads
import { useForm } from 'react-hook-form';
type FormValues = {
name: string;
email: string;
message: string;
};
export default function ContactForm() {
const { register, handleSubmit, formState: { isSubmitting } } = useForm<FormValues>();
const onSubmit = async (data: FormValues) => {
const response = await fetch('https://formbase.dev/s/YOUR_FORM_ID', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
redirect: 'manual',
});
if (response.status !== 303 && !response.ok) {
throw new Error('Submission failed');
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} placeholder="Name" required />
<input {...register('email')} type="email" placeholder="Email" required />
<textarea {...register('message')} required />
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send'}
</button>
</form>
);
}
JSON is great when you want structured data or arrays.import { useForm } from 'react-hook-form';
type UploadValues = {
name: string;
resume: FileList;
};
export default function UploadForm() {
const { register, handleSubmit, formState: { isSubmitting } } = useForm<UploadValues>();
const onSubmit = async (data: UploadValues) => {
const formData = new FormData();
formData.append('name', data.name);
formData.append('resume', data.resume[0]);
const response = await fetch('https://formbase.dev/s/YOUR_FORM_ID', {
method: 'POST',
body: formData,
redirect: 'manual',
});
if (response.status !== 303 && !response.ok) {
throw new Error('Upload failed');
}
};
return (
<form onSubmit={handleSubmit(onSubmit)} encType="multipart/form-data">
<input {...register('name')} placeholder="Name" required />
<input {...register('resume')} type="file" />
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Uploading...' : 'Upload'}
</button>
</form>
);
}
Use FormData when files are involved.
Client submissions get a 303 redirect response. Treat 303 as success or submit from a server action for JSON.