Wire your v0 form to email in one step
v0 by Vercel is superb at generating shadcn/ui forms. Because those forms are usually dropped into Next.js, you have two clean options: a client-side fetch, or a one-function Server Action that forwards to the endpoint. Either way you skip building and securing a route handler.
v0 produces the form component but no backend, so submissions have nowhere to go. Point the form at a MakeTheForm endpoint, or use a tiny Server Action that forwards to it, and each submission is spam-filtered, emailed to you, and saved in a searchable inbox with no route handler to build.
The minimal integration
v0 output lands in Next.js. This Server Action forwards the submission to your endpoint: no /api route, no database. A plain client-side fetch works too if the form is a client component.
// app/contact/actions.ts
'use server';
export async function submitContact(prevState, formData) {
const response = await fetch('https://mtform.co/f/your-form-key', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(Object.fromEntries(formData)),
});
const result = await response.json();
if (!response.ok) {
return { ok: false, message: result.error?.message ?? 'Unable to send your message.' };
}
return { ok: true, message: 'Thanks. We got your message.' };
}
// app/contact/page.tsx
'use client';
import { useActionState } from 'react';
import { submitContact } from './actions';
export default function ContactPage() {
const [state, action, pending] = useActionState(submitContact, null);
return (
<form action={action}>
<input name="name" placeholder="Name" required />
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required />
<input name="_mtf_honeypot" tabIndex={-1} autoComplete="off" aria-hidden="true"
style={{ position: 'absolute', left: '-9999px' }} />
<button type="submit" disabled={pending}>{pending ? 'Sending…' : 'Send'}</button>
{state && <p role={state.ok ? 'status' : 'alert'}>{state.message}</p>}
</form>
);
}A Server Action: no API route needed. Returns a serializable result the form renders inline.
Step by step
- 01
Create the endpoint
Create a MakeTheForm form to get your endpoint URL and inbox.
- 02
Generate in v0, then wire it
Keep your v0-generated form and paste the prompt above. It adds a Server Action (or fetch) that forwards to your endpoint.
- 03
Verify and ship
Verify the recipient once, deploy to Vercel, and submit to confirm the inbox and email delivery.
Copy this prompt into your AI builder
Paste it as-is. It pins the exact endpoint, spam field, and success/error behavior so the agent wires the form correctly instead of inventing a backend or leaking a key.
Wire the contact form in this v0 / Next.js project to this MakeTheForm endpoint:
https://mtform.co/f/your-form-key
Requirements:
- Prefer a Server Action that forwards the fields to the endpoint via fetch as JSON. Do NOT create a database or a custom /api route beyond the action itself.
- Keep the existing shadcn/ui markup and styling. Only add submit wiring.
- Disable the submit button while pending (useActionState / pending state).
- Treat any HTTP 2xx as success: show an inline success message and reset the form.
- On error, surface the JSON response’s error.message and keep the user’s input.
- Include a hidden honeypot input named exactly "_mtf_honeypot" (position:absolute;left:-9999px, tabIndex={-1}, autoComplete="off", aria-hidden="true"). Never display:none, never required.
- The endpoint URL is public and safe in client code. Do NOT invent or request an API key.Common pitfalls
Building a full /api route + Resend
v0 will offer to add a route handler and an email SDK. That’s a mail integration and a deliverability reputation to manage. Forwarding from a one-line Server Action to the endpoint avoids all of it.
Client component vs Server Action mismatch
If the form is a client component, a Server Action still works when imported, but a plain fetch is simpler. Don’t mix a "use server" action into a "use client" file directly, import it.
Troubleshooting
| Symptom | What is happening | Fix |
|---|---|---|
| action not firing | The form uses onSubmit instead of the Server Action’s action prop. | Bind the action to <form action={...}> or call the action from your submit handler. |
| 422 | A required field configured on the form wasn’t sent. | Check the field names match your form config; the JSON error lists the offending field. |
Frequently asked questions
→Does v0 handle form submissions?
v0 generates the form UI (usually shadcn/ui) but not a backend. To actually receive submissions you either add server code or point the form at a hosted endpoint: the endpoint handles email, spam, and storage for you.
→Should I use a Server Action or a client fetch?
Either works. A Server Action keeps the endpoint call server-side and pairs well with useActionState for pending UI; a client fetch is the least code. Both forward to the same endpoint.
→Do I need a database for a v0 form?
No. The endpoint stores each submission in a searchable, exportable inbox, so a contact or lead form needs no database of your own.
Keep going
- Form backend for AI-built websites
- Next.js contact form
- Lovable contact form
- Bolt.new contact form backend
Last reviewed July 23, 2026
Ship this form for real
Create an endpoint, paste it in, and watch the first submission land in your inbox, in under three minutes.
Create free endpoint