How to add a form to your Gatsby site
Add a contact form to your Gatsby site with no backend. A complete React example that submits with fetch, shows an inline success message, and reports errors accessibly.
This guide shows you how to add a Gatsby contact form that collects submissions and emails you
on every new one — without writing or hosting any backend code. Because Gatsby is React, the form
is a normal React component: we’ll start with a plain form that works on its own, then submit it
with fetch so users stay on the page and see an inline confirmation.
Create your form endpoint in FormBackend
Go create a login and create a new form endpoint in FormBackend. Give it a name you can remember, for example “Gatsby Contact Form”. Once it’s created, open the Setup tab and copy the unique form URL — you’ll paste it into the code below.
Create a new Gatsby app
If you already have a Gatsby site, skip to the next section. Otherwise install the CLI and create one:
npm install -g gatsby-cli gatsby new formbackend-gatsby cd formbackend-gatsby gatsby develop
Your site is now running at http://localhost:8000.
Create the contact page
Create src/pages/contact.js. We’ll start with a plain form — note the JSX-specific attribute
htmlFor instead of for (for is a reserved word in JavaScript, so React renames it):
import * as React from "react" import Layout from "../components/layout" import Seo from "../components/seo" export default function ContactPage() { return ( <Layout> <Seo title="Contact us" /> <h1>Contact us</h1> <form method="POST" action="https://www.formbackend.com/f/your-form-id"> <div> <label htmlFor="name">Name</label> <input type="text" id="name" name="name" required /> </div> <div> <label htmlFor="email">Email</label> <input type="email" id="email" name="email" required /> </div> <div> <label htmlFor="message">Message</label> <textarea id="message" name="message" required></textarea> </div> <button type="submit">Send message</button> </form> </Layout> ) }
Replace your-form-id with the URL from your form’s Setup tab. Visit http://localhost:8000/contact,
fill the form out, and the submission shows up under Submissions in FormBackend. The form
already works — it just redirects to FormBackend’s thank-you page on submit.
Submit without a page refresh
For a smoother experience, submit in the background with fetch and swap the form for an inline
message. Update src/pages/contact.js:
import * as React from "react" import { useState } from "react" import Layout from "../components/layout" import Seo from "../components/seo" export default function ContactPage() { const [status, setStatus] = useState("idle") async function handleSubmit(event) { event.preventDefault() setStatus("submitting") const form = event.currentTarget const response = await fetch(form.action, { method: "POST", body: new FormData(form), headers: { accept: "application/json" }, }) if (response.ok) { form.reset() setStatus("success") } else { setStatus("error") } } return ( <Layout> <Seo title="Contact us" /> <h1>Contact us</h1> {status === "success" ? ( <p role="status">Thanks! Your message has been sent.</p> ) : ( <form method="POST" action="https://www.formbackend.com/f/your-form-id" onSubmit={handleSubmit} > <div> <label htmlFor="name">Name</label> <input type="text" id="name" name="name" required /> </div> <div> <label htmlFor="email">Email</label> <input type="email" id="email" name="email" required /> </div> <div> <label htmlFor="message">Message</label> <textarea id="message" name="message" required></textarea> </div> <button type="submit" disabled={status === "submitting"}> {status === "submitting" ? "Sending…" : "Send message"} </button> {status === "error" && <p role="alert">Something went wrong — please try again.</p>} </form> )} </Layout> ) }
A few things worth noting:
event.preventDefault()stops the browser’s default full-page submission.- The browser’s built-in
FormDatareads every field straight from the form, so you don’t need a piece ofuseStateper input — the inputs stay uncontrolled. - The
accept: application/jsonheader tells FormBackend to return JSON instead of an HTML page. - Because the
<form>keeps a validactionandmethod, it still works if JavaScript fails to load — progressive enhancement for free.
Want client-side validation with messages under each field? Gatsby is React, so the React Hook Form + Zod example in our React guide drops straight into this page.
Note: Gatsby has been largely superseded by newer React frameworks. If you’re starting a new project, consider Next.js or Astro instead. Existing Gatsby sites continue to work fine with FormBackend.
Configure notifications and integrations
Your Gatsby form is now collecting submissions. Here’s what to set up in FormBackend:
- Email notifications: Get notified every time someone submits, with optional file attachments
- Auto-reply emails: Automatically confirm receipt to the person who submitted
- Spam protection: Built-in spam filtering is active by default. Add Cloudflare Turnstile for extra security
- Integrations: Route submissions to Slack, Google Sheets, Notion, or any URL via webhooks
- File uploads: Accept file uploads by adding
enctype="multipart/form-data"to your form - Redirect after submission: Send users to a specific page on your site
Guides for other React-based frameworks: React, Next.js. Or see all framework guides.
Add a form backend to your site in minutes
Connect any HTML form to FormBackend and start collecting submissions — no backend code required.
Start free