Form Integration

A React contact form with no backend to deploy

You don't need Express or a serverless function. Your React component sends the form to a Formbold endpoint, and each entry lands in your inbox and your dashboard. Free covers 250 submissions a month.

Contact Form for React

What does a working React contact form look like?

Like this: one component, useState for the loading and status text, and a fetch call to your endpoint. It runs in Vite, Remix, Create React App or any other React setup. Replace FORM_ID and drop it into a page.

react.tsx
// src/components/ContactForm.tsx
import { useState } from "react";

export default function ContactForm() {
  const [loading, setLoading] = useState(false);
  const [responseMessage, setResponseMessage] = useState("");

  async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    setLoading(true);

    const formData = new FormData(event.currentTarget);
    const json = JSON.stringify(Object.fromEntries(formData));

    try {
      const response = await fetch("https://formbold.com/s/FORM_ID", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
        body: json,
      });
      const result = await response.json();

      if (response.ok) {
        setResponseMessage(result.message || "Message sent successfully!");
        (event.target as HTMLFormElement).reset();
      } else {
        setResponseMessage(result.message || "Something went wrong");
      }
    } catch (error) {
      setResponseMessage("Failed to send message.");
    } finally {
      setLoading(false);
      setTimeout(() => setResponseMessage(""), 4000);
    }
  }

  return (
    <form onSubmit={handleSubmit} className="flex flex-col gap-4 max-w-[452px] mx-auto">
      {/* Email Address */}
      <div className="flex flex-col gap-2">
        <label htmlFor="email" className="text-sm font-medium text-gray-700">
          Email Address<span className="ml-0.5 text-red-500">*</span>
        </label>
        <input
          type="email"
          name="email"
          id="email"
          placeholder="Enter your email"
          required
          className="max-h-11 w-full rounded-xl border border-gray-300 bg-white px-4 py-2.5 text-base text-gray-900 transition-all placeholder:text-[#9ca3af] focus:border-[#5750f1] focus:ring-4 focus:ring-[#5750f1]/10 focus:outline-none"
        />
      </div>

      {/* Subject */}
      <div className="flex flex-col gap-2">
        <label htmlFor="subject" className="text-sm font-medium text-gray-700">
          Subject<span className="ml-0.5 text-red-500">*</span>
        </label>
        <input
          type="text"
          name="subject"
          id="subject"
          placeholder="Enter your subject"
          required
          className="max-h-11 w-full rounded-xl border border-gray-300 bg-white px-4 py-2.5 text-base text-gray-900 transition-all placeholder:text-[#9ca3af] focus:border-[#5750f1] focus:ring-4 focus:ring-[#5750f1]/10 focus:outline-none"
        />
      </div>

      {/* Message */}
      <div className="flex flex-col gap-2">
        <label htmlFor="message" className="text-sm font-medium text-gray-700">
          Message<span className="ml-0.5 text-red-500">*</span>
        </label>
        <textarea
          name="message"
          id="message"
          rows={5}
          placeholder="Type your message..."
          required
          className="w-full resize-y rounded-xl border border-gray-300 bg-white px-4 py-3 text-base text-gray-900 transition-all placeholder:text-[#9ca3af] focus:border-[#5750f1] focus:ring-4 focus:ring-[#5750f1]/10 focus:outline-none"
        />
      </div>

      {/* Submit Button */}
      <button
        type="submit"
        disabled={loading}
        className="flex h-12 w-full cursor-pointer items-center justify-center gap-2 rounded-xl bg-[#111827] px-5 py-3 text-base font-medium text-white shadow-xs transition-all hover:bg-black active:scale-[0.99] disabled:opacity-50"
      >
        {loading ? "Submitting..." : "Submit Message"}
      </button>

      {/* Response Notice */}
      {responseMessage && (
        <p className="rounded-xl p-3 text-center text-sm font-medium bg-gray-50 text-gray-800">
          {responseMessage}
        </p>
      )}
    </form>
  );
}
How it works

How do I connect a React form to Formbold?

Create the form, paste the endpoint, test it. That's the whole setup.

1

Create a form

In All Forms, click Create New Form. Name it after the page it sits on, like Website Contact, so you can find it later.

2

Paste the endpoint into fetch

Copy https://formbold.com/s/FORM_ID from the Form Setup tab. Give every input a name attribute, because that name becomes the field label.

3

Submit once and check

Send a test from your dev server. The entry appears in Submissions within seconds, and the email follows.

Features

What do you get without writing a backend?

The parts you'd otherwise build on Node: somewhere to store entries, an email sender, spam filtering and integrations.

No server code

No Express app, no Lambda and no SMTP credentials in your repo. The component is the only code you own.

Spam handled outside React

Basic Spam Filter is a toggle in Settings > Spam & Security. The component submits with fetch, so Custom reCAPTCHA is the captcha to use.

File uploads

From Starter: up to 5 files of 10 MB each per submission, with higher limits on Premium and Business.

Pipe entries to Slack or Sheets

On Starter and higher, connect Slack, Telegram, Notion or Google Sheets, or send a webhook.

FAQs

React form questions

The questions that come up once the form is on the page.

No. Formbold receives the request, stores it and sends the email. Your app only needs the fetch call shown above.

Still have a question?

The docs cover setup, framework guides, integrations and spam protection, step by step.