Back to blog
guides

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.

J
Jesper Christiansen
Header graphic that shows the Gatsby logo

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 FormData reads every field straight from the form, so you don’t need a piece of useState per input — the inputs stay uncontrolled.
  • The accept: application/json header tells FormBackend to return JSON instead of an HTML page.
  • Because the <form> keeps a valid action and method, 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:

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