---
title: "Building Contact Forms for Next.js Sites with Formspree"
description: "In this guide, we’ll show you how to add a contact form to a Next.js website using Formspree and deploy it to Netlify for free. We’ll be using Next.js but the concepts can be applied to any React framework, such as Gatsby or Remix."
source: "https://www.netlify.com/blog/nextjs-react-forms-formspree/"
last_updated: "2026-07-10T20:29:38.000Z"
---
One of the most common obstacles to launching a Jamstack site is wiring up your forms to a backend server or function. “Why not just use Netlify Forms?” you ask. Definitely you can! However, for React projects, there are a few extra steps required to [make sure the Netlify form parser identifies your form](https://www.netlify.com/blog/2017/07/20/how-to-integrate-netlifys-form-handling-in-a-react-app/). Today we’ll be discussing an alternative, [formspree-react](https://help.formspree.io/hc/en-us/articles/360055613373), a form handling library built specifically for React projects.

Formspree-react provides a React-friendly `useForm` hook that handles form submits, updates the form state and displays server-side errors. As a bonus, once your form is connected to [Formspree](https://formspree.io/) you can add [plugins for Mailchimp, Google Sheets, and more](https://help.formspree.io/hc/en-us/sections/360007580793-Plugins) with just a few clicks.

In this guide, we’ll show you how to add a contact form to a Next.js website using Formspree and deploy it to Netlify. We’ll be using Next.js but the concepts can be applied to any React framework, such as Gatsby or Remix. At the end you should have a working React contact form in your Next.js project that sends you email notifications. You can find a demo of the final project at [https://formspree-example-netlify.netlify.com](https://formspree-example-netlify.netlify.com). The project repository is hosted on github at [https://github.com/formspree/formspree-example-netlify](https://github.com/formspree/formspree-example-netlify).

## Prerequisites

To follow this guide you’re going to need a Formspree account, which you can [sign up for free right here](https://formspree.io/register), and an existing web project built with Next.js. If you don’t have a Next.js project yet, you can create one by running:

```
npx create-next-app <app-name>
```

If you’re not familiar with Next.js then [check out the official documentation](https://nextjs.org) to learn more.

## Adding a form to Next.js

First, we’re going to build a form component that you can place anywhere in your Next.js site. Create a new file called `contactForm.js` inside of your `components` directory. Here’s an example of the file structure including your new component:

```
projectRoot/├─ components/|  └─ contactForm.js└─ pages/   └─ index.js
```

Next we’ll build the form component using `@formspree/react`. Install it with:

```
npm install --save @formspree/react
```

Then paste the following code snippet into the `contactForm.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} />
      <textarea id="message" name="message" />      <ValidationError prefix="Message" field="message" errors={state.errors} />
      <button type="submit" disabled={state.submitting}>        Submit      </button>      <ValidationError errors={state.errors} />    </form>  );}
```

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 ([https://github.com/formspree/formspree-example-netlify](https://github.com/formspree/formspree-example-netlify)) you can see an example of how to apply styles to the form.

The component can now be imported in your Next.js pages like so:

```
import ContactForm from "../components/contactForm";
```

And inserted into the page as a React component:

```
<ContactForm />
```

For this tutorial, we’ll add the `<ContactForm />` component to the bottom of our `index.js` file, but you can add it wherever you like!

Once you’ve saved your additions use your build or deployment workflow to either run the Next.js site locally or deploy it to where-ever you host your site (such as Netlify) to see the newly added form.

When you attempt to submit the form, you’ll currently see an error message:

![Error message from Formspree](https://cdn.sanity.io/images/o0o2tn5x/production/c2815a1907ab79456447e750a390faedd5b30608-509x215.png?w=450)

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](https://formspree.io). If you don’t have an account yet you can sign up [here](https://formspree.io/register).

To start, create a new form with the **+** 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**.

![Form dialogue box for Formspree creation](https://cdn.sanity.io/images/o0o2tn5x/production/a4bb24dd5e8a31bc1bbffc2adcf35d7ef6e60c16-566x449.png?w=450)

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.

![Integration Code for creating a React App with Formspree](https://cdn.sanity.io/images/o0o2tn5x/production/bb067d5bf59328a63406b1c72a2c0d55b4ffea9d-1726x912.png?w=450)

The code we used to create the `contactForm.js` component is almost identical to the React example code on the integration page. We just need to update the `useForm` hook.

To do that, copy the 8 character “hash id” from the new form’s endpoint URL and replace the `YOUR_FORM_ID` placeholder like so:

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

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

**That’s it, you’re done!**

## Deploying to Netlify

To deploy this project to Netlify, create a new site and select “import an existing project”. Pick GitHub, and give Netlify access to your GitHub repositories. Select the appropriate repository.

If you’ve just been following along and don’t have a repository set up, you can just click the “Deploy to Netlify” button below!

[![deploy to netlify button](https://www.netlify.com/img/deploy/button.svg "Deploy to Netlify")](https://app.netlify.com/start/deploy?repository=https://github.com/formspree/formspree-example-netlify#NEXT_PUBLIC_FORM=YOUR_FORM_ID)

## Bonus Tip: Production and Development Forms

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

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

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

Now you need to ensure the variable is loaded when you develop locally. Create a `.env.local` file at the root of your project and add the environment variable, with your form’s [hashid](https://help.formspree.io/hc/en-us/articles/360015130174-Getting-your-form-s-hashid-) as the value:

```
NEXT_PUBLIC_FORM=xxxxxxxx
```

Caveats:

-   In Next.js, only environment variables prefixed with `NEXT_PUBLIC_` are accessible to client side javascript code. Don’t render secret environment variables, like API keys, into your frontend code.
-   Be sure to add your `.env.local` file to `.gitignore` so you don’t accidentally check-in any secrets to version control. Your Formspree endpoint isn’t secret, but nevertheless, it’s a good practice.

Now, as you’re working locally, your form will submit to the endpoint in your `.env.local` file. When you’re ready to ship to production, create a new production form in the Formspree dashboard, and set the `NEXT_PUBLIC_FORM` environment variable to the new form endpoint in your hosting config (eg. Netlify).

You can read more about [how to use environment variables in Next.js](https://nextjs.org/docs/basic-features/environment-variables), and [how to set build environment variables in Netlify](https://docs.netlify.com/configure-builds/environment-variables/).

### Share

-   [X (fka Twitter)](https://twitter.com/intent/tweet?text=Build Contact Forms for Next.js Sites with Formspree&url=https://www.netlify.com/blog/nextjs-react-forms-formspree/)
-   [LinkedIn](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fwww.netlify.com%2Fblog%2Fnextjs-react-forms-formspree%2F)
-   [Facebook](https://www.facebook.com/sharer.php?u=https://www.netlify.com/blog/nextjs-react-forms-formspree/)
-   [Bluesky](https://bsky.app/intent/compose?text=Build Contact Forms for Next.js Sites with Formspree+https://www.netlify.com/blog/nextjs-react-forms-formspree/)

* * *

### Tags

-   [React](/blog/tags/react/)
-   [Next.js](/blog/tags/next.js/)
-   [Next.js](/blog/tags/nextjs/)
-   [form handling](/blog/tags/form-handling/)
-   [Forms](/blog/tags/forms/)
-   [Netlify forms](/blog/tags/netlify-forms/)

## Keep reading

![](/_astro/845f56b97caab6a45cb246045596bcea5b061f88-1800x946_1pIadI.webp)

Guides & Tutorials December 16, 2021

[

### Accepting form submissions without a server

](/blog/2021/12/16/accepting-form-submissions-without-a-server/)

-   ![Profile picture of Phil Hawksworth](/_astro/9cfe2bbe94bcb555ce3ac7683ac7905ad3fd32b1-400x400_Z18H6R1.webp)
    
    Phil Hawksworth
    

![](/images/blog-fallback-thumbnail.svg)

Guides & Tutorials July 20, 2017

[

### How to Integrate Netlify’s Form Handling in a React App

](/blog/2017/07/20/how-to-integrate-netlifys-form-handling-in-a-react-app/)

-   ![Profile picture of Irene Morente](/_astro/222926eb2886ded67a3d2c77720f50a968342aea-80x80_ZpomnS.webp)
    
    Irene Morente
    

## Recent posts

News & Announcements June 25, 2026

[

### Netlify Functions, designed for Agent Experience

](/blog/netlify-functions-designed-for-agent-experience)

-   ![Profile picture of Eduardo Bouças](/_astro/52958f21e8450baf6d8e60302341a984e220c0cd-512x512_13VDlu.webp)
    
    Eduardo Bouças
    

News & Announcements June 24, 2026

[

### How we measure Netlify’s Agent Experience

](/blog/how-we-measure-netlify-agent-experience)

-   ![Profile picture of Sean Roberts](/_astro/bbf2243f8171dbddd80ab2103622106cef84d125-512x512_Z1d2LKE.webp)
    
    Sean Roberts
    

Guides & Tutorials May 15, 2026

[

### How to build a real-time AI chatbot in minutes with Netlify Agent Runners (no backend)

](/blog/how-to-build-a-real-time-ai-chatbot-in-minutes-with-netlify-agent-runners-no-backend)

-   ![Profile picture of Nahrin Jalal](/_astro/f0e7c8f227a03fe58340c99ef5439d5a896c0733-272x272_Z23kDpD.webp)
    
    Nahrin Jalal
    

![](/_astro/3f255b372fa958df35802666ee33b4609b2d71bd-1200x1586_1VtE2D.webp)

### How do the best dev and marketing teams work together?

[Access the report](https://www.netlify.com/reports/2024-leadership-trend-report/access/)