Get the next one in your inbox.
One email when we publish something worth reading. No spam — appropriately enough, we'd know.
No API route, no server action boilerplate, no email service to wire up. Here's the fastest correct way to ship a working contact form in a Next.js App Router project.

Get an instant summary, key takeaways, action items, and answers to your questions about this article.
In a Next.js App Router project, you can ship a fully working contact form without writing an API route or server action by pointing the form's action attribute at a FormBridge endpoint — the form works as a plain HTML form, or you can wire it to a client component for a custom submit experience.
The fastest way to ship a working contact form in Next.js App Router isn't a server action — it's not writing backend code at all.
Since a FormBridge endpoint accepts a standard POST, a plain server-rendered form component works with no client-side code:
export default function ContactForm() {
return (
<form action="https://app.formbridge.ai/api/forms/fb_8h2k9p" method="POST">
<input name="full-name" placeholder="Full name" required />
<input name="email" type="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required />
<button type="submit">Send</button>
</form>
)
}
No API route. No server action. No email service to configure. The browser submits directly to the endpoint.
If you want a loading state or to stay on the page after submit, make it a client component and fetch the same endpoint:
'use client'
import { useState } from 'react'
export default function ContactForm() {
const [status, setStatus] = useState<'idle' | 'sending' | 'sent'>('idle')
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
setStatus('sending')
const data = new FormData(e.currentTarget)
await fetch('https://app.formbridge.ai/api/forms/fb_8h2k9p', {
method: 'POST',
body: data,
headers: { Accept: 'application/json' },
})
setStatus('sent')
}
return (
<form onSubmit={handleSubmit}>
<input name="full-name" placeholder="Full name" required />
<input name="email" type="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required />
<button disabled={status === 'sending'}>
{status === 'sent' ? 'Sent!' : 'Send'}
</button>
</form>
)
}
Once the endpoint receives a submission, FormBridge runs its normal pipeline — spam check, email notification, and (if configured) an autoresponder back to whoever filled out the form. See the field naming guide if you want hyphenated field names to auto-format nicely in your inbox.
No. Pointing your form's action attribute directly at a form-backend endpoint means the browser submits straight to that service — no Next.js API route or server action required for basic use.
Yes — call fetch() against the same endpoint from an onSubmit handler if you want custom loading states or client-side validation, instead of relying on the browser's native form submission.
One email when we publish something worth reading. No spam — appropriately enough, we'd know.