Published on

Create a Contact Form with Formspree in a Next.js Project

Authors

📨 Introduction

When your Next.js site is static (like on Vercel), there’s no backend available to handle contact form submissions.
That’s where Formspree comes in — it lets you receive messages from your visitors directly via email, without writing a single line of backend code.

Formspree’s official useForm hook simplifies the entire process: it handles form submission, loading states, and errors for you.

In this tutorial, we’ll:

  • Install the official @formspree/react package
  • Create a styled and responsive contact form
  • Deploy it on Vercel

⚙️ 1. Install the Formspree package

First, install the official React package:

npm install @formspree/react

✉️ 2. Create your Formspree account

Go to formspree.io

Click Start for free

Once logged in, create a new form

You’ll get a unique form endpoint, for example:

https://formspree.io/f/mayvpqdr

Keep the Formspree ID (mayvpqdr) — you’ll need it in the next step.

🧩 3. Create the form using useForm

Create a component: components/ContactForm.jsx

"use client";

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

export default function ContactForm() {
  const [state, handleSubmit] = useForm("mayvpqdr"); // Replace with your Formspree ID

  if (state.succeeded) {
    return (
      <p className="text-green-600 text-center font-medium">
        ✅ Message successfully sent!
      </p>
    );
  }

  return (
    <form
      onSubmit={handleSubmit}
      className="mx-auto max-w-md space-y-4 p-6 rounded-xl border border-gray-200 dark:border-gray-700 shadow-md"
    >
      <div>
        <label htmlFor="email" className="block font-medium mb-1">
          Email address
        </label>
        <input
          id="email"
          type="email"
          name="email"
          required
          className="w-full rounded-md border border-gray-300 p-2 dark:bg-gray-800 dark:border-gray-600"
        />
        <ValidationError prefix="Email" field="email" errors={state.errors} />
      </div>

      <div>
        <label htmlFor="message" className="block font-medium mb-1">
          Message
        </label>
        <textarea
          id="message"
          name="message"
          rows="5"
          required
          className="w-full rounded-md border border-gray-300 p-2 dark:bg-gray-800 dark:border-gray-600"
        />
        <ValidationError prefix="Message" field="message" errors={state.errors} />
      </div>

      <button
        type="submit"
        disabled={state.submitting}
        className="w-full rounded-md bg-primary-600 p-2 text-white hover:bg-primary-700 transition"
      >
        {state.submitting ? "Sending..." : "Send"}
      </button>
    </form>
  );
}

🧱 4. Create the /contact page

Create a file at app/contact/page.jsx:

import ContactForm from "@/components/ContactForm";

export default function ContactPage() {
  return (
    <section className="py-12 px-6">
      <h1 className="text-3xl font-bold mb-6 text-center">Contact</h1>
      <p className="text-center text-gray-500 mb-8">
        Have a question or a collaboration idea? Drop me a message 👇
      </p>
      <ContactForm />
    </section>
  );
}

🏁 Conclusion

And that’s it 🎉 In just a few lines, you now have a fully functional React contact form that works perfectly on Vercel, without any backend, and with built-in error handling through the useForm hook.