Guide Thumbnail

React Forms with Gatsby

In this guide we’ll show you how to add a contact form to your Gatsby website using Formspree.

Formspree is a form backend that’s an ideal companion for static site generators, letting you get on with building and deploying your Jamstack website.

At the end of this guide you should have a working React contact form in your Gatsby site that sends you email notifications. Check out a live demo of the final project. The project repository is hosted on GitHub.


To follow this guide you’re going to need a Formspree account, which you can sign up for free right here, and an existing web project built with Gatsby. If you don’t have a Gatsby project yet, you can create one by running:

npx gatsby new gatsby-formspree-starter
cd gatsby-formspree-starter
npm run develop

If you’re not familiar with Gatsby then check out the official documentation to learn more.

Adding a form to Gatsby

Since Gatsby uses React, the form code you’re about to add can be neatly wrapped up in a React component. Create a new file called contact-form.js inside of your components directory. Here’s an example of the file structure including your new component:

├─ gatsby-config.js
└─ src/
   ├─ components/
   │  └─ contact-form.js
   ├─ pages/
   └─ templates/

Next we’ll build the form component using a helper library from Formspree, @formspree/react. This library contains a useForm hook to simplify the process of handling form submission events and managing form state. Install it with:

npm install --save @formspree/react

Then paste the following code snippet into the contact-form.js file:

import { useForm, ValidationError } from "@formspree/react";

export default function ContactForm() {
  const [state, handleSubmit] = useForm("YOUR_FORM_ID");

  if (state.succeeded) {
    return <p>Thanks for your submission!</p>;

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email">Email Address</label>
      <input id="email" type="email" name="email" />
      <ValidationError prefix="Email" field="email" errors={state.errors} />

      <label htmlFor="message">Message</label>
      <textarea id="message" name="message" />
      <ValidationError prefix="Message" field="message" errors={state.errors} />
      <button type="submit" disabled={state.submitting}>
      <ValidationError errors={state.errors} />

A few notes:

  • Currently the form contains a placeholder YOUR_FORM_ID. We’ll replace this with our own form endpoint in a bit.
  • The useForm hook returns a state object and a handleSubmit function which we pass to the onSubmit form attribute. Combined, these provide a way to submit the form data via AJAX and update form state depending on the response received.
  • ValidationError components are helpers that display error messages for field errors, or general form errors (if no field attribute is provided).
  • For the sake of clarity, this form doesn’t include any styling, but in the github project ( you can see an example of how to apply styles to the form.

The component can now be imported into your website’s index page, or any page, like so:

import ContactForm from "../components/contact-form";

And inserted into the page as a React component:

<ContactForm />

Once you’ve saved your additions either run the Gatsby site locally with npm run develop or deploy it to your live environment to see the newly added form.

At this point, when you attempt to submit the form, you’ll see an error message:

React form submit error

Oops! We get an error because we still have the placeholder YOUR_FORM_ID in the form action. Let’s fix this by setting up a form endpoint to accept our form submissions.

Creating a form endpoint

Next we’ll create a form endpoint using Formspree. If you don’t have an account yet you can sign up here.

To start, create a new form with the +New form button, call it Contact form and update the recipient email to the email where you wish to receive your form submissions. Then click Create Form.

Formspree new form modal

You’ll then be presented with the integration options for your new Formspree form. Note that Formspree provides you with a wide range of implementation examples such as React, AJAX and regular HTML.

React form integration code

The code we used to create the contact-form.js component is almost identical to the React example code on the integration page. We just need to update the action URL.

Finally copy the 8 character “hash id” from the new form’s endpoint URL and replace the YOUR_FORM_ID placeholder in your contact form component.

const [state, handleSubmit] = useForm("abcd1234"); // <-- your form ID

Now when you fill out the form and submit, you should see a success message.

That’s it, you’re done!

Bonus Tip: Environment Variables

Sometimes you want to test out a new design locally, but don’t want to affect your production data. With Gatsby you can switch between development and production forms using environment variables.

In your contact-form.js component, you can replace your form ID with an environment variable in your useForm hook like so:

const [state, handleSubmit] = useForm(process.env.GATSBY_CONTACT_FORM);

Note: we must prefix the environment variable with GATSBY_ to ensure it’s accessible to front-end code.

Now create a .env.development file at the root of your project with the following content:


Replace xxxxxxxx with your form’s hashid.

Now, as you’re working locally, your form will submit to the endpoint in your .env.development file. When you’re ready to ship to production, you can create a new production form in the Formspree dashboard, and set the GATSBY_CONTACT_FORM variable to the new form id in production.

You can read more about how to use environment variables in Gatsby, and how to set build environment variables in Gatsby Cloud.

Got Feedback?