How to Build a Next.js Contact Form

Last updated: March 2026

Adding a contact form to Next.js usually means setting up API routes, configuring an email service, and handling server-side logic. But there's a simpler way. Use a form backend service to handle submissions so you can keep your Next.js app lean.

This guide covers both the App Router (Next.js 13+) and Pages Router approaches.

The simple approach: HTML form + form backend

Instead of building an API route, point your form's action attribute to a form backend endpoint. FormWit receives the submission, stores it, and sends you an email notification. Your Next.js app stays purely frontend.

App Router example (Next.js 13+)

Create a contact page at app/contact/page.tsx:

export default function ContactPage() {
  return (
    <main>
      <h1>Contact Us</h1>
      <form
        action="https://app.formwit.com/api/s/YOUR_FORM_ID"
        method="POST"
      >
        <input type="text" name="_gotcha" style={{ display: 'none' }} />

        <label htmlFor="name">Name</label>
        <input type="text" id="name" name="name" required />

        <label htmlFor="email">Email</label>
        <input type="email" id="email" name="email" required />

        <label htmlFor="message">Message</label>
        <textarea id="message" name="message" required />

        <button type="submit">Send Message</button>
      </form>
    </main>
  );
}

This works as a standard HTML form submission. No client-side JavaScript needed.

Pages Router example

Create pages/contact.tsx:

export default function ContactPage() {
  return (
    <main>
      <h1>Contact Us</h1>
      <form
        action="https://app.formwit.com/api/s/YOUR_FORM_ID"
        method="POST"
      >
        <input type="text" name="_gotcha" style={{ display: 'none' }} />

        <label htmlFor="name">Name</label>
        <input type="text" id="name" name="name" required />

        <label htmlFor="email">Email</label>
        <input type="email" id="email" name="email" required />

        <label htmlFor="message">Message</label>
        <textarea id="message" name="message" required />

        <button type="submit">Send Message</button>
      </form>
    </main>
  );
}

AJAX submission with React state

For a better UX without page reload, handle submission with fetch:

'use client';

import { useState, FormEvent } from 'react';

export default function ContactForm() {
  const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle');

  async function handleSubmit(e: FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setStatus('sending');

    const form = e.currentTarget;
    const data = new FormData(form);

    try {
      const res = await fetch(form.action, {
        method: 'POST',
        body: data,
      });

      if (res.ok) {
        setStatus('sent');
        form.reset();
      } else {
        setStatus('error');
      }
    } catch {
      setStatus('error');
    }
  }

  return (
    <form
      action="https://app.formwit.com/api/s/YOUR_FORM_ID"
      method="POST"
      onSubmit={handleSubmit}
    >
      <input type="text" name="_gotcha" style={{ display: 'none' }} />

      <label htmlFor="name">Name</label>
      <input type="text" id="name" name="name" required />

      <label htmlFor="email">Email</label>
      <input type="email" id="email" name="email" required />

      <label htmlFor="message">Message</label>
      <textarea id="message" name="message" required />

      <button type="submit" disabled={status === 'sending'}>
        {status === 'sending' ? 'Sending...' : 'Send Message'}
      </button>

      {status === 'sent' && <p>Message sent successfully!</p>}
      {status === 'error' && <p>Something went wrong. Please try again.</p>}
    </form>
  );
}

Note the 'use client' directive. This component uses React state and event handlers, so it must be a Client Component in the App Router.

Setting up FormWit

  1. Create a free account
  2. Create a new form in the dashboard
  3. Copy the form endpoint URL
  4. Replace YOUR_FORM_ID in the examples above

Why not use an API route?

You can build a Next.js API route that sends emails via Resend, SendGrid, or Nodemailer. But that means:

  • Managing API keys and environment variables
  • Writing email sending logic and error handling
  • No submission dashboard or storage
  • No built-in spam protection
  • More code to maintain

A form backend service handles all of this out of the box. You write zero server-side code.

Summary

Adding a contact form to Next.js doesn't require API routes or a backend. Point your form to a FormWit endpoint and you get email notifications, submission storage, spam protection, and a dashboard, with zero server-side code.

FormWit's free plan includes unlimited forms and 100 submissions per month. Get started free.

Related guides: React contact form · HTML contact form · Vercel contact form · Spam protection methods · Contact form templates

Frequently asked questions

Does FormWit work with Next.js App Router?

Yes. The form works in both Server Components (as a plain HTML form) and Client Components (with useState and fetch for AJAX submission). For Client Components, add the 'use client' directive at the top of the file. No API routes or Server Actions required.

Should I use Server Actions or FormWit?

Server Actions require you to write server-side code, manage email sending, and build your own submission storage. FormWit handles all of that out of the box. Use Server Actions if you need custom server-side logic like database writes or payment processing. Use FormWit for standard contact forms where you just need email notifications and a dashboard.

Does it work with Next.js static export?

Yes. When you use output: 'export' in your Next.js config, the form still works because submissions go from the browser directly to FormWit's endpoint. No API routes or serverless functions are involved, so there is nothing that breaks in a static export.

Want to skip the setup?

FormWit gives you a form endpoint in 60 seconds. Free plan, no credit card.

Create Free Form

Need a form fast?

Build one visually with our free HTML form generator — no coding required.

Try the Form Generator →

Add FormWit to your Next.js site

Add a contact form to your site in 30 seconds. No backend code required.

Try FormWit Free