Form Integration

A Gatsby contact form without Gatsby Functions

Your Gatsby site stays static. The form posts to a Formbold endpoint, so there's no Gatsby Function or serverless handler to write, and each entry reaches your inbox.

Contact Form for Gatsby

What does the Gatsby component look like?

A React component for src/components/ContactForm.tsx. It sends the fields with fetch and shows a status line. Import it into any page under src/pages and replace FORM_ID.

gatsby.tsx
// src/components/ContactForm.tsx
import React, { 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 (err) {
      setResponseMessage("Failed to submit form.");
    } 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>

      {/* Status 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 add a contact form to Gatsby?

Three steps, and nothing changes in gatsby-config.

1

Set up a form in Formbold

Sign up, then Create New Form. Free allows 10 forms and 250 submissions a month.

2

Add the endpoint to the component

Replace the placeholder URL in the fetch call with https://formbold.com/s/FORM_ID from Form Setup.

3

Test in gatsby develop

Submit once on localhost:8000 and check Submissions before you deploy.

Features

What does Formbold take off your plate?

The server side of the form. Gatsby keeps building static pages, and Formbold stores, filters and delivers the entries.

No Gatsby Functions

Nothing in src/api, so the site can live on any static host.

Captcha for a fetch form

This component submits with fetch, so pair Basic Spam Filter with Custom reCAPTCHA and your own v2 keys.

File uploads

Starter and higher. Send FormData instead of JSON so the files come along.

Leads into HubSpot or Sheets

Starter and higher send entries to HubSpot, Google Sheets, Slack, Zapier or Make, or to a webhook.

FAQs

Builds, hosting and files

Mostly questions about builds and hosting.

No. The component posts to Formbold, which works the same on any host.

Still have a question?

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