Handle form submissions in React without a backend

React apps often need to collect user input — contact forms, feedback, signups. Instead of building an API endpoint, you can submit forms directly to LazyForms using fetch(). Works with any React setup: CRA, Vite, Next.js client components, Remix.

The problem: React has no built-in form backend

React is a frontend library. To process a form submission, you typically need:

  1. A backend API route to receive the data
  2. Email/notification logic
  3. A database to store submissions
  4. Spam protection

LazyForms handles all of this. You just POST form data to our endpoint.

Basic form submission with fetch

export function ContactForm() { const [status, setStatus] = useState('idle');

async function handleSubmit(e) { e.preventDefault(); setStatus('sending');

const data = new FormData(e.target); const res = await fetch('https://api.lazyforms.com/f/YOUR_ACCESS_KEY', { method: 'POST', body: data, });

const json = await res.json(); setStatus(json.success ? 'sent' : 'error'); }

if (status === 'sent') return <p>Thanks! We'll be in touch.</p>;

return ( <form onSubmit={handleSubmit}> <input type="text" name="name" placeholder="Name" required /> <input type="email" name="email" placeholder="Email" required /> <textarea name="message" placeholder="Message" required /> <button disabled={status === 'sending'}> {status === 'sending' ? 'Sending...' : 'Send'} </button> </form> ); } ```

Sending JSON instead of FormData

LazyForms also accepts JSON. Useful when you have structured data:

const res = await fetch('https://api.lazyforms.com/f/YOUR_ACCESS_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Jane Doe',
    email: 'jane@example.com',
    message: 'Hello from React!',
  }),
});

Both FormData and JSON are supported — use whichever fits your component logic.

Adding honeypot spam protection

Add a hidden field that bots will fill but users won't:

<input
  type="text"
  name="_honey"
  style={{ display: 'none' }}
  tabIndex={-1}
  autoComplete="off"
/>

Enable honeypot checking in your LazyForms form settings. Any submission with a non-empty `_honey` field gets rejected.

TypeScript version

export function ContactForm() { const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle');

async function handleSubmit(e: FormEvent<HTMLFormElement>) { e.preventDefault(); setStatus('sending');

const data = new FormData(e.currentTarget); const res = await fetch('https://api.lazyforms.com/f/YOUR_ACCESS_KEY', { method: 'POST', body: data, });

const json = await res.json(); setStatus(json.success ? 'sent' : 'error'); }

if (status === 'sent') return <p>Message sent!</p>; if (status === 'error') return <p>Something went wrong. Try again.</p>;

return ( <form onSubmit={handleSubmit}> <input type="text" name="name" required /> <input type="email" name="email" required /> <textarea name="message" required /> <button disabled={status === 'sending'}> {status === 'sending' ? 'Sending...' : 'Send message'} </button> </form> ); } ```

Works with any React framework

  • Create React App / Vite — use directly in any component
  • Next.js — use in client components (`'use client'`)
  • Remix — use in client-side handlers
  • Gatsby — use in any page or component

No API routes, no serverless functions, no environment variables needed for basic form submission.

Ready to get started?

Get your form endpoint in seconds. Free forever.

Enter your email to receive your form action URL · No password needed

More guides