Next.js makes it easy to add a Route Handler and call Resend or SendGrid from the server. For a single contact form, that is still your auth, spam filtering, provider keys, and deploy surface. This guide shows a Next.js contact form without API routes: a Client Component posts to a form API with @formsreach/react, so you skip the mail server and keep the App Router simple.
The same hook works with the Pages Router and with static export. You still own the UI; FormsReach owns delivery.
What you’ll need
- A Next.js app (App Router examples below; Pages Router notes included)
- Node 18+ for the package install
- A free FormsReach API key
Optional later: WhatsApp on Pro or Agency after email works.
Why skip the API route for one form?
Common Next.js patterns for contact forms:
- Route Handler + email provider - secrets stay on the server, but you own rate limits, spam, retries, and provider downtime.
- Server Action that sends mail - same ownership, different syntax.
- Form API from the browser - public form key, domain allowlisting, managed spam and channels.
FormsReach is option 3. Credits meter usage; Free includes 500 credits/month for submissions, email, free webhooks, and workflow rules. WhatsApp via Meta’s WhatsApp Business API is on Pro and Agency only. Product background: send form submissions to WhatsApp.
If you already know React outside Next, see React contact form without a backend.
Step 1: Install the package
npm install @formsreach/react
useFormsReach(apiKey) returns { submit, submitting } for a form onSubmit handler.
Step 2: Add a Client Component (App Router)
Hooks need the browser. Mark the form file with 'use client' and keep it out of Server Components that only render static markup.
// npm install @formsreach/react
'use client';
import { useFormsReach } from '@formsreach/react';
export default function ContactForm() {
const { submit, submitting } = useFormsReach('YOUR_API_KEY');
return (
<form onSubmit={submit}>
<input type="text" name="name" required />
<input type="email" name="email" required />
<textarea name="message" required />
<button type="submit" disabled={submitting}>Submit Form</button>
</form>
);
}
Use real name attributes. The hook serializes the form and posts to FormsReach (including spam-protection fields the SDK manages). Replace YOUR_API_KEY with the dashboard key. Prefer an env var inlined at build time for production, for example process.env.NEXT_PUBLIC_FORMSREACH_KEY, and allowlist your domain in FormsReach.
Import the component from a page or layout:
import ContactForm from '@/components/ContactForm';
export default function ContactPage() {
return (
<main>
<h1>Contact</h1>
<ContactForm />
</main>
);
}
No app/api/.../route.ts is required for this path.
Step 3: Handle submitting state
submitting is true while the request is in flight. Disable the button and optionally show “Sending…”. Configure a thank-you redirect in FormsReach, or handle success in your product flow. Prefer one clear success path so users do not double-submit.
Step 4: Static export and hosting notes
| Setup | What to know |
|---|---|
| Default Next hosting (Node or serverless) | Client Component still posts from the browser; no Route Handler needed |
output: 'export' static export |
Works if the form is client-side and the key is available at build time |
| Domain allowlisting | Add the live host in FormsReach after deploy |
You do not need SSR for the form to work. The browser owns the POST.
Step 5: Pages Router variant
Create a component without 'use client' (Pages is client by default for interactive components) or use the same hook in any page/component that runs in the browser:
// npm install @formsreach/react
import { useFormsReach } from '@formsreach/react';
export default function ContactForm() {
const { submit, submitting } = useFormsReach('YOUR_API_KEY');
return (
<form onSubmit={submit}>
<input type="text" name="name" required />
<input type="email" name="email" required />
<textarea name="message" required />
<button type="submit" disabled={submitting}>Submit Form</button>
</form>
);
}
Step 6: Notifications
A typical path is 1 credit for an accepted submission and 1 credit for an email notification. Webhooks cost 0 credits on every plan.
Enable WhatsApp in the dashboard on Pro or Agency when sales wants chat-speed replies. You do not rewrite the Next form when you turn the channel on.
Zero-JS HTML form instead? See contact form on a static website without a backend or the Astro contact form guide.
Troubleshooting
Hook error / useFormsReach is not a function
Confirm @formsreach/react is installed. In App Router, the file that calls the hook must include 'use client'.
Submit does nothing
Check Network for api.formsreach.com. Bad keys and domain blocks return 4xx.
Env key is empty in production
NEXT_PUBLIC_* vars must be set at build time for static and many host pipelines. Redeploy after changing them.
Hydration warnings
Do not render different form markup on server vs client. Keep the form structure stable; only disable the button from submitting after mount.
Double submissions
Use disabled={submitting} and do not wrap submit in your own extra fetch.
Ship the form without owning the mail path
A Next.js contact form without API routes is a Client Component plus a form API. FormsReach gives you the endpoint, spam filtering, email on Free (500 credits/month), free webhooks, and WhatsApp on Pro and Agency via Meta’s Business API. Keep Route Handlers for app logic that actually needs a server, not for one contact box.
