Get the next one in your inbox.
One email when we publish something worth reading. No spam — appropriately enough, we'd know.
Build a React contact form from shadcn/ui Input, Textarea, and Button components, and submit it to a FormBridge endpoint with a single fetch call.

Get an instant summary, key takeaways, action items, and answers to your questions about this article.
Build the form with shadcn/ui's Input, Textarea, and Button components as usual, then in the onSubmit handler call fetch() with the form's FormData against your FormBridge endpoint URL — that fetch call is the entire integration.
There's no FormBridge package to install for this. That's the whole point of a headless form backend: shadcn/ui gives you the Input, Textarea, and Button primitives; FormBridge just needs a fetch call in your submit handler.
'use client';
import { useState } from 'react';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Button } from '@/components/ui/button';
const ENDPOINT = 'https://app.formbridge.ai/api/forms/fb_8h2k9p';
export function ContactForm() {
const [status, setStatus] = useState('idle');
async function handleSubmit(e) {
e.preventDefault();
setStatus('sending');
const formData = new FormData(e.currentTarget);
try {
const res = await fetch(ENDPOINT, {
method: 'POST',
headers: { Accept: 'application/json' },
body: formData,
});
setStatus(res.ok ? 'sent' : 'error');
if (res.ok) e.currentTarget.reset();
} catch {
setStatus('error');
}
}
if (status === 'sent') {
return <p className="text-sm text-muted-foreground">Thanks for reaching out — we'll reply soon.</p>;
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<Input name="full-name" placeholder="Full name" required />
<Input type="email" name="email" placeholder="Email" required />
<Textarea name="message" placeholder="How can we help?" required rows={5} />
<Button type="submit" disabled={status === 'sending'}>
{status === 'sending' ? 'Sending...' : 'Send message'}
</Button>
{status === 'error' && <p className="text-sm text-red-600">Something went wrong — try again.</p>}
</form>
);
}
FormData works whether or not the form eventually grows a file input, so it's a safer default than hand-building a JSON body. If you'd rather send JSON, set a Content-Type: application/json header and stringify a plain object of your field names — FormBridge doesn't care which encoding you use, it reads whatever field names you send.
The actual "backend" behavior — a recipient list, a honeypot field, a custom autoresponder — lives entirely in the FormBridge dashboard, scoped to this one endpoint. Swap the endpoint ID and the same handful of lines work for a signup form, a feedback form, or a demo request form elsewhere in the same app.
No. FormBridge doesn't ship a React SDK, hooks, or shadcn/ui component — you use shadcn/ui's own primitives and send the data with a standard fetch() call.
Either works. FormData is a safer default since it also handles file inputs if you add one later; JSON works fine for text-only fields as long as you set a Content-Type: application/json header.
One email when we publish something worth reading. No spam — appropriately enough, we'd know.