Blog

shadcn/ui Form + FormBridge in 5 Minutes

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.

← Back to the blog
AI Powered

Explore this article with AI

Get an instant summary, key takeaways, action items, and answers to your questions about this article.

Choose your AI assistant
ChatGPT Perplexity
Quick answer

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.

The component

'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>
  );
}

Why FormData instead of JSON

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.

Nothing else to configure in code

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.

Frequently asked

Is there an official FormBridge component for shadcn/ui or React?

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.

Should I send the form as FormData or JSON?

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.

Key facts

  • FormBridge has no JavaScript SDK or npm package — integration is a plain fetch() POST request to your endpoint URL.
  • A FormBridge endpoint accepts both multipart/form-data and JSON request bodies.
  • Submissions return a JSON response of the form { ok: true, id: 'sub_...' } on success.

Terms in this post

FormData
A built-in browser API that serializes an HTML form's fields, including files, into a request body, without any extra library.

Get the next one in your inbox.

One email when we publish something worth reading. No spam — appropriately enough, we'd know.