Blog ·
Next.js contact form with Server Actions
Build a Next.js App Router contact form with FormHeron: plain form action versus Server Action, honeypot field, JSON fetch, and origin lists.
- nextjs
- react
Next.js Server Actions look like the perfect place to send mail. For contact forms they are often unnecessary. A form that posts straight to FormHeron needs no Route Handler and no Action.
Recommended: direct form action
export default function ContactPage() {
return (
<form action="https://formheron.com/f/YOUR_KEY" method="POST">
<input type="email" name="email" required />
<textarea name="message" required />
<input
type="text"
name="_hp"
tabIndex={-1}
autoComplete="off"
aria-hidden="true"
style={{ position: "absolute", left: "-9999px" }}
/>
<button type="submit">Send</button>
</form>
);
}Works in the App Router with zero server code on your side. Full notes: Next.js platform page and template.
When a Server Action helps
Use an Action when you must enrich the payload server-side, check auth, or hide multi-step workflow state. The Action should still call FormHeron with fetch rather than inventing SMTP. Keep the payload flat strings only.
JSON fetch example
const res = await fetch("https://formheron.com/f/YOUR_KEY", {
method: "POST",
headers: {
"content-type": "application/json",
accept: "application/json",
},
body: JSON.stringify({ email, message, _hp: "" }),
});
const data = await res.json();
// data.code: quota_exceeded | rate_limited | origin_not_allowed | …Add your production and Vercel preview origins to the form allowlist. Error codes: docs.
Spam and redirects
Always include the honeypot. Optional Turnstile with your Cloudflare keys. Set redirect_url to an https URL on an allowed origin for branded thanks pages. More: stop form spam.
App Router gotchas
- Do not put Turnstile secrets in NEXT_PUBLIC_ vars
- Server Components can render the form; no client bundle required for the basic path
- If you use Server Actions, still include _hp in the FormData you forward
- Preview deployments need their hostnames on the origin allowlist when locked down
Testing locally
Add http://localhost:3000 (or your port) to allowed origins while developing, or leave the allowlist open temporarily. Never ship an open allowlist if you care about other sites embedding your key. Submit with and without _hp filled to confirm spam isolation.
Also useful: React platform, Vercel platform, docs.
FormHeron is a form backend with spam controls, a lead inbox, HMAC webhooks and Slack. Free plan: 250 submissions/month. Leads stored in the EU (Amsterdam); operated from India. No raw IPs.