Next.js multi-step form

Build a multi-step form in Next.js with no backend. Collect fields across steps in client state, then post once to Formpaste at the end. Copy the wizard code and add your access key.

Step 1 of 3

How to build it

  1. Create a form in your Formpaste dashboard and copy its access key.
  2. Copy the multi-step component below into a client component.
  3. Replace YOUR_ACCESS_KEY_HERE with your access key, then deploy.

Prompt for your AI coding agent

Add a Formpaste multi-step form to my Next.js app. Use a client component that collects name, email and message across several steps in local state with Next and Back buttons, then POSTs once at the end as application/x-www-form-urlencoded (URLSearchParams) to https://api.formpaste.com/submit with a hidden access_key. Do not post on every step and do not add an API route.

Or connect the Formpaste MCP server to Claude Code, Codex, Cursor, Claude Desktop, VS Code, or any mcp-remote client. See the setup guide.

Next.js gotchas

Next.js gives you API routes, but for simpler forms, spinning up a route handler is overkill. Formpaste handles the backend so you can keep your Next.js app focused on the frontend. Works with both App Router and Pages Router.

Client component for the fetch version

The onSubmit fetch needs to run in the browser, so put the form in a file with "use client" at the top. A plain <form action> with a redirect works in a server component too if you do not need JS.

No API route needed

You do not need /api/contact. Posting straight to the Formpaste endpoint keeps the app static-friendly and deployable anywhere, including static export.

Server Actions are optional

You can wire a Server Action, but it is not required and adds a server round-trip. The direct POST is simpler and works on static hosting.

Edge runtime and file uploads

If a route uses the Edge runtime, multipart form data handling differs from Node. For file-upload forms, post directly to Formpaste from the client so your own runtime never parses the upload.

Static export friendly

Because the form posts to an external endpoint, output: export keeps working. No server code of yours runs at request time.

What to get right

One form, several screens

A multi-step form is a single submission split across steps in the UI. Collect all the fields in client state, then post once at the end.

Route per step is optional

You can give each step its own URL for back-button support, or keep it all on one page with local state. Both post the same final payload.

Post the whole payload once

Do not post on every step. Gather the fields as the user advances and send one request when they finish.

How it works

Three simple steps for form submissions to land in your email inbox.
Create your form, set your form's action URL to Formpaste's endpoint, and add your access key.

1

Create your form

Sign up, verify your email, and create an access key in your dashboard.

2

Copy the code

Paste the snippet above into your project. Pick CSS or Tailwind to match your site.

"use client";
import { useState } from "react";

const ENDPOINT = "https://api.formpaste.com/submit";

export function MultiStepForm() {
  const [step, setStep] = useState(0);
  const [data, setData] = useState({ name: "", email: "", message: "" });
  const set = (k: string, v: string) => setData((d) => ({ ...d, [k]: v }));

  async function submit(e: React.FormEvent) {
    e.preventDefault();
    const body = new URLSearchParams({ access_key: "YOUR_ACCESS_KEY_HERE", ...data });
    await fetch(ENDPOINT, { method: "POST", body });
  }

  return (
    <form onSubmit={submit}>
      {step === 0 && (
        <input value={data.name} onChange={(e) => set("name", e.target.value)} placeholder="Name" required />
      )}
      {step === 1 && (
        <input type="email" value={data.email} onChange={(e) => set("email", e.target.value)} placeholder="Email" required />
      )}
      {step === 2 && (
        <textarea value={data.message} onChange={(e) => set("message", e.target.value)} placeholder="Message" />
      )}
      {step < 2 ? (
        <button type="button" onClick={() => setStep((s) => s + 1)}>Next</button>
      ) : (
        <button type="submit">Submit</button>
      )}
    </form>
  );
}
3

Add your access key

Replace the placeholder with your access key to start receiving submissions.

<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY_HERE">

Next.js multi-step form FAQ

Paste a form. Get submissions.

Instantly without setting up any backend, servers or databases. 250 submissions/mo free - no credit card.

Other Next.js forms