Contact form for Next.js
The reflex in Next.js is to write an API route that relays the form to an email service. You can skip that entire layer: post directly to FormDock from the client. It works with the App Router, the Pages Router, and — unlike an API route — with output: 'export' static builds.
app/contact/ContactForm.tsx
"use client";
import { useState } from "react";
export function ContactForm() {
const [status, setStatus] = useState<"idle" | "sending" | "sent" | "error">("idle");
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setStatus("sending");
const data = Object.fromEntries(new FormData(e.currentTarget));
const res = await fetch(process.env.NEXT_PUBLIC_FORM_ENDPOINT!, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
setStatus(res.ok ? "sent" : "error");
}
if (status === "sent") return <p>Thanks — we got your message.</p>;
return (
<form onSubmit={handleSubmit}>
<input name="name" required placeholder="Name" />
<input name="email" type="email" required placeholder="Email" />
<textarea name="message" placeholder="Message" />
<button disabled={status === "sending"}>Send</button>
</form>
);
}- Put the endpoint in NEXT_PUBLIC_FORM_ENDPOINT (e.g. https://formdock.app/f/your-key) so staging and production can use different forms.
- No API route means nothing to deploy, no serverless cold starts, and static export keeps working.
- Prefer zero JavaScript? A plain HTML form with action + method works in any server or client component — see the HTML guide.
- Lock CORS to your domains in form settings before going live.
Get your endpoint: create a free FormDock account (100 submissions/month), add a form, and swap your-form-key for the real key.