How to Add a Working Contact Form to Next.js Without an API Route with onsubmit.dev (form backend)
If you need a contact form in Next.js, you don't necessarily need to build an API route, deploy a serverless function, configure SMTP, or maintain a database. With onsubmit.dev (form backend), the nextjs-onsubmit package can handle the submission endpoint for you while your app stays focused on the frontend.
The usual Next.js approach
A contact form often starts with some simple JSX:
export default function ContactForm() {
return (
<form>
<input type="text" name="name" placeholder="Your name" />
<input type="email" name="email" placeholder="you@example.com" />
<textarea name="message" placeholder="Your message" />
<button type="submit">Send</button>
</form>
);
}
But this doesn't actually send the data anywhere.
One option is to add an API endpoint:
app/
api/
contact/
route.ts
Then you need to parse and validate requests, send an email or Slack notification, handle errors, and potentially add spam protection.
For a simple contact form, that's quite a bit of backend code.
Install nextjs-onsubmit
Instead, install the Next.js package for onsubmit.dev (form backend):
npm install nextjs-onsubmit
The package lets your form submit to a hosted form backend instead of an API route in your own Next.js application.
That means you don't need:
/api/contact
or:
app/api/contact/route.ts
And there's no serverless function to maintain.
Create the contact form
Here's a minimal example:
import { OnSubmitForm } from "nextjs-onsubmit";
export default function ContactForm() {
return (
<OnSubmitForm formId="YOUR_FORM_ID" successMessage="Thanks! We'll be in touch.">
<input
type="text"
name="name"
placeholder="Your name"
required
/>
<input
type="email"
name="email"
placeholder="you@example.com"
required
/>
<textarea
name="message"
placeholder="How can we help?"
required
/>
<button type="submit">Send message</button>
</OnSubmitForm>
);
}
The important part here is that every value you want included in the submission has a name.
For example:
<input name="email" type="email" />
produces an email field, while:
<textarea name="message" />
produces a message field.
You can therefore add fields without designing a request payload:
<OnSubmitForm formId="YOUR_FORM_ID" successMessage="Thanks! We'll be in touch.">
<input name="name" required />
<input name="email" type="email" required />
<select name="topic">
<option value="sales">Sales</option>
<option value="support">Support</option>
<option value="other">Other</option>
</select>
<textarea name="message" required />
<button type="submit">Submit</button>
</OnSubmitForm>
No Next.js API route required
The useful difference is what you don't have to write.
There's no route handler like this:
export async function POST(request: Request) {
const body = await request.json();
// validate input
// configure email provider
// send email
// handle provider failure
// return response
}
Your Next.js project can remain a frontend application, while onsubmit.dev (form backend) receives and processes the form submission externally.
This is especially handy for sites where forms are infrastructure rather than core application logic: portfolios, landing pages, documentation sites, waitlists, and marketing sites are good examples.
Send submissions to email or Slack
After connecting/configuring your form with the service, submissions can be delivered to the destination you actually monitor.
For email, a submission containing:
name: Ada
email: ada@example.com
topic: sales
message: I'd like to learn more.
can arrive in your inbox without your Next.js application having to integrate with an email API.
If your team works in Slack, you can configure Slack delivery instead, so new form submissions appear in the appropriate channel.
In either case, your frontend remains essentially the same:
Visitor
β
Next.js form
β
Hosted form backend
β
Email / Slack
That separation also means email or Slack credentials don't need to live in client-side code.
Add normal HTML validation
You can still use native browser validation:
<OnSubmitForm formId="YOUR_FORM_ID" successMessage="Thanks! We'll be in touch.">
<input
name="name"
type="text"
minLength={2}
required
/>
<input
name="email"
type="email"
required
/>
<textarea
name="message"
minLength={10}
maxLength={2000}
required
/>
<button type="submit">Send</button>
</OnSubmitForm>
This gives users immediate feedback for common problems while avoiding a custom API endpoint just to receive the successful submission.
For security-sensitive or business-critical applications, remember that client-side validation alone should never be treated as authoritative validation.
When this pattern makes sense
A hosted form backend is a good fit when the purpose of the form is simply to get information from a visitor into email, Slack, or another workflow.
If submissions instead need to trigger application-specific authorization, database transactions, billing operations, or other sensitive business logic, implementing the relevant server-side code yourself will usually make more sense.
For straightforward contact forms, though, removing an otherwise single-purpose API route can make a Next.js project considerably simpler.
That's the idea behind onsubmit.dev (form backend): accept Next.js form submissions without turning a basic contact form into another backend service to build and maintain.
Top comments (0)