AI app builder · v0
Make your v0 contact form actually send email
v0 generates a polished shadcn/ui form fast — react-hook-form validation, sensible layout, a toast on submit — but that toast usually fires from a handler that logs to the console with nowhere real to send the data. Wiring it up "properly" means asking v0 for a Next.js Route Handler, an email provider API key, and a Vercel environment variable to store it, which is a lot of backend for "add a contact form."
Point the form at a PostTo endpoint instead and it delivers to your inbox immediately — no Route Handler, no provider key, no environment variable. Paste one prompt into v0's chat and the existing form is rewired, its shadcn/ui components and validation schema untouched.
Free plan, no credit card required.
Three steps, a few minutes
Create an endpoint
Sign up free and create a PostTo endpoint with your destination email. You get an endpoint URL immediately — no Vercel integration or API key exchange with v0 required.
Paste the prompt into v0
Give v0 the prompt below with your endpoint URL swapped in. It edits the existing form's submit handler in place — the shadcn/ui components, react-hook-form schema, and styling stay exactly as they are.
Test it in the preview
Submit the form from v0's live preview. The email lands in your inbox, and the submission shows up in the PostTo dashboard immediately.
The prompt and the code
Replace YOUR_TOKEN
with the endpoint URL from your dashboard.
Update the contact form's onSubmit handler to send its data to PostTo instead
of just logging it to the console. POST the validated form values as JSON to
https://postto.dev/api/v1/send/YOUR_TOKEN with a
"Content-Type: application/json" and "Accept: application/json" header. Show
the existing success toast if the response is ok, and the existing error
toast otherwise. Add a hidden "_hp" honeypot field to the form schema that
real visitors never see. Keep the shadcn/ui components, validation schema,
and styling unchanged.
'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Button } from '@/components/ui/button';
import { toast } from 'sonner';
const formSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
message: z.string().min(1),
_hp: z.string().optional(), // honeypot: bots fill this in, humans never see it
});
export function ContactForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: { name: '', email: '', message: '', _hp: '' },
});
async function onSubmit(values: z.infer<typeof formSchema>) {
const res = await fetch('https://postto.dev/api/v1/send/YOUR_TOKEN', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(values),
});
toast(res.ok ? 'Message sent!' : 'Something went wrong.');
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField control={form.control} name="name" render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl><Input {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<FormField control={form.control} name="email" render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl><Input type="email" {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<FormField control={form.control} name="message" render={({ field }) => (
<FormItem>
<FormLabel>Message</FormLabel>
<FormControl><Textarea {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<input type="text" {...form.register('_hp')} tabIndex={-1} autoComplete="off" className="hidden" />
<Button type="submit">Send message</Button>
</form>
</Form>
);
}
Good to know
No Route Handler needed
v0 often reaches for a Next.js Route Handler plus a transactional email provider key (Resend, SendGrid) stored as a Vercel environment variable for "send an email" features. PostTo replaces all of that — the fetch call above is the entire integration.
Token only — never signed mode here
This request runs client-side in the shipped bundle, so use the plain endpoint URL with its token. Never enable signed mode for a browser-side integration — the HMAC secret would ship inside your app's JavaScript, visible to anyone.
Spam protection is already wired in
The hidden _hp field is a honeypot — humans never see it, bots fill it in, and PostTo silently discards those submissions. Server-side baseline filtering (heuristics and optional AI classification) runs on every submission after that, so you don't need a CAPTCHA to start.
Frequently asked questions
- Can I just tell v0 to add this instead of editing code myself?
- Yes — paste the prompt above into v0's chat with your endpoint URL swapped in. v0 treats it like any other feature request and rewires the existing onSubmit handler.
- Does this conflict with v0's Vercel deployment or shadcn/ui setup?
- No. PostTo only replaces the "send this form as an email" piece — your shadcn/ui components, Tailwind config, and Vercel deployment are unaffected.
- Will this survive syncing to my Next.js repo or a redeploy?
- Yes — it's a plain fetch call inside your component code, so it persists through "Add to Codebase," a git push, and Vercel redeploys the same as any other code v0 writes.
- What does it cost?
- The free plan includes 50 submissions per month with the full dashboard — no credit card required. Paid plans start at $9/month.
Add a working form to your v0 app today
Create an endpoint, paste the prompt, and see your first submission in the dashboard — all on the free plan.
Try PostTo free