Best for
Founders and developers who need a validated multi-field form — contact, onboarding, settings — working in Next.js App Router without writing boilerplate
Stack
A ready-made Next.js Forms UI you can fork, run, and customize with the prompt pack below.
What's actually inside
The honest engineer's breakdown — what the Next.js Formstemplate does, how it's wired, and where it's opinionated.
The Next.js Forms template is a practical starting point for any validated multi-field form in Next.js App Router. The Form Container wraps a react-hook-form useForm instance connected to a Zod schema via zodResolver — the validation layer is already in place. Inside the container you get Text Inputs (shadcn/ui Input + Label pairs with FormField, FormControl, and FormMessage for inline errors), a Select Field for dropdown options, a Textarea for multi-line input, and a Submit Button that shows a loading spinner while isSubmitting is true from react-hook-form.
On successful submission, a Success Toast fires via shadcn/ui Toast (or Sonner if the project uses it). The Server Action wiring is scaffolded but left as a prompt step — the brief calls this out as intentional since the destination (email, database, webhook) varies per use case. What's already done is the hardest part: field registration, Zod schema validation, inline error display, loading state, and success feedback.
One honest caveat: V0's default Toast import references a shadcn/ui registry path (`https://ui.shadcn.com/r/styles/new-york-v4/toast.json`) that does not exist as a standalone registry item in the current registry. The fix is straightforward — switch to Sonner which ships with many V0 projects — but expect to hit this on first fork. The Zod/react-hook-form validation mode also defaults to `onSubmit`, so users see no errors while typing. The medium prompt covers switching to `onBlur` mode.
Key UI components
Form Containershadcn/ui Form wrapping react-hook-form useForm instance with zodResolver
Text Inputsshadcn/ui Input + Label pairs with FormField, FormControl, FormMessage for inline error display
Select Fieldshadcn/ui Select registered with react-hook-form for dropdown option selection
Textareashadcn/ui Textarea for multi-line input such as bio, message, or notes fields
Submit Buttonshadcn/ui Button showing a loading spinner while react-hook-form isSubmitting is true
Success Toastshadcn/ui Toast or Sonner notification shown after successful form submission
Libraries it leans on
react-hook-formAll form state management, field registration, submission handling, and isSubmitting state
zodField validation schemas — required, min length, email format, regex, and file size constraints
shadcn/uiForm, Input, Select, Textarea, Button, and Toast/Sonner components
Fork it and get it running
Forking this template gets you a working validated form in minutes. The real customization is in the Zod schema and the Server Action — both are straightforward prompt steps once the fork is live.
Fork the template
Open https://v0.dev/chat/community/fpb5RFRWVpZ and click the "Fork" button in the top-right corner. You land in a new V0 project with the Form Container, Text Inputs, Select Field, Textarea, Submit Button, and Success Toast all loaded in the preview. Sign in to v0.dev first if prompted.
You should see: A new V0 project opens with the multi-field form visible in the preview panel.
Update field labels in Design Mode
Press Option+D to open Design Mode. Click the form title and field labels to update them for your use case — rename 'Name' to 'Full Name', change 'Message' to 'Project Description', update the Submit Button text to 'Send Request'. Update the Select Field option labels to match your dropdown options. All Design Mode changes are free and do not spend V0 credits.
Tip: Design Mode works best for text and color changes. For structural changes like adding or removing fields, use the chat prompt.
You should see: The form preview shows your custom labels and button text.
Define your Zod validation schema
In the V0 chat, paste this prompt: "Add a Zod schema validating: name (required, min 2 chars), email (valid email format), message (required, min 10 chars, max 500 chars); wire zodResolver to the useForm instance; display inline error messages below each field using FormMessage from shadcn/ui Form components". V0 will update the schema and wire the inline error messages through the FormField and FormMessage components.
Tip: Add any field-specific rules you need in the same prompt — e.g. 'phone must be 10 digits' or 'company name optional, max 100 chars'.
You should see: Submitting the form with invalid values shows inline error messages below each field via FormMessage.
Wire the Server Action for submission
Prompt V0: "Add a Server Action in app/actions/submit-form.ts that receives the form data and sends an email via Resend using RESEND_API_KEY from the Vars panel; return { success: true } on success and { error: 'message' } on failure". Then open the Vars panel and add RESEND_API_KEY (get it from resend.com → API Keys). The Server Action receives a typed object directly from react-hook-form's handleSubmit — not FormData.
Tip: If you do not have a Resend account yet, substitute 'log the form data to the console and return { success: true }' in the prompt for quick testing.
You should see: Submitting the form with valid data triggers the Server Action and shows the Success Toast.
Deploy and test end-to-end
Click Share → Publish → "Publish to Production". Your form goes live on a Vercel subdomain in under 60 seconds. Open the live URL, fill in the form, submit it, and check that the Success Toast appears and the email (or console log) arrives. If RESEND_API_KEY is needed in production, also add it in your Vercel Dashboard → Settings → Environment Variables for the Production scope.
You should see: A submission on the live Vercel URL triggers the Server Action, delivers the email (or logs), and shows the Success Toast on screen.
The prompt pack
Copy-paste these straight into v0's chat to customize the Next.js Formstemplate. Each one names this template's own components — no generic filler.
Add file upload field with size validation
Adds a file attachment field to the Form Container with client-side size validation via Zod, so oversized files are rejected before any upload attempt.
Add a shadcn/ui Input type='file' field to the Form Container between the Textarea and the Submit Button, labeled 'Attachment (optional)'. Register it with react-hook-form using a ref. Add a Zod validation rule: z.instanceof(File).optional().refine(f => !f || f.size < 5_000_000, 'Max file size is 5MB'). Display the selected file name below the input as a text-sm note after selection. Show a FormMessage error if the file exceeds 5MB.
Switch validation to run on blur
Changes the react-hook-form validation timing so users see inline FormMessage errors as they tab through fields, rather than seeing all errors at once on submit.
Update the useForm configuration in the Form Container to add mode: 'onBlur' so validation errors appear as soon as the user leaves a field, not just on submit. Keep the zodResolver wiring unchanged. Also update the Submit Button label from its current text to 'Submit' if it is currently set to a placeholder string.
Add conditional fields based on Select value
Makes the form context-aware so Partnership and Support inquiries show the relevant extra fields without the user having to scroll past irrelevant inputs.
Add a shadcn/ui Select field labeled 'Inquiry type' at the top of the Form Container with options: Contact, Partnership, Support. Use useWatch({ control, name: 'inquiryType' }) to conditionally render additional fields below it: when 'Partnership' is selected show Company Name (shadcn/ui Input) and Website URL (shadcn/ui Input); when 'Support' is selected show Order Number (shadcn/ui Input) and Issue Description (shadcn/ui Textarea). Add a CSS transition (transition-all duration-200) on the conditional section wrapper for a smooth appearance.Add multi-step wizard with progress indicator
Breaks the form into manageable steps so users are not overwhelmed by a long single-page form — each step validates before advancing.
Split the Form Container into a 3-step wizard: Step 1 collects name and email (the existing Text Inputs); Step 2 collects the Select field, Textarea, and optional file upload; Step 3 shows a read-only review of all entered values plus the Submit Button. Track the current step with useState. On 'Next', validate only the current step's fields using trigger(['name', 'email']) from react-hook-form before advancing. Show a Progress indicator above the form displaying 'Step 1 of 3', 'Step 2 of 3', etc. with a visual bar.
Wire to Resend for email delivery
Delivers form submissions to your inbox via Resend using a typed Server Action — no FormData parsing required since react-hook-form's handleSubmit passes a plain object.
Install the resend npm package. Add RESEND_API_KEY to the Vars panel. Create app/actions/send-email.ts as a Server Action with 'use server' at the top. Inside, construct the email HTML from the form values (name, email, message, inquiry type) and call resend.emails.send({ from: 'noreply@yourdomain.com', to: 'you@yourdomain.com', subject: 'New form submission from ' + data.name, html: emailHtml }). Return { success: true } on success. In the Form Container client component, call the action from handleSubmit and show the Success Toast on { success: true }.Save submissions to Supabase with CAPTCHA guard
Stores every form submission to Supabase and protects the endpoint from bots using Cloudflare Turnstile verification in the Server Action before any database write.
Create a form_submissions table in Supabase: CREATE TABLE form_submissions (id UUID DEFAULT gen_random_uuid() PRIMARY KEY, name TEXT, email TEXT, message TEXT, inquiry_type TEXT, created_at TIMESTAMPTZ DEFAULT NOW()); ALTER TABLE form_submissions ENABLE ROW LEVEL SECURITY; CREATE POLICY 'anon can insert' ON form_submissions FOR INSERT WITH CHECK (true). Install @marsidev/react-turnstile; add NEXT_PUBLIC_TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY to Vars; render the Turnstile widget at the bottom of the Form Container above the Submit Button. In the Server Action app/actions/submit-form.ts, validate the Turnstile token with a POST to https://challenges.cloudflare.com/turnstile/v0/siteverify before calling supabase.from('form_submissions').insert(data) using the SUPABASE_SERVICE_ROLE_KEY (server-only, no NEXT_PUBLIC_ prefix).Gotchas when you extend it
The failures people actually hit when they push this template past its defaults — and the exact fix for each.
`The component at https://ui.shadcn.com/r/styles/new-york-v4/toast.json was not found`Why: V0 generates a Toast reference that no longer exists as a standalone shadcn/ui registry item. The toast component path changed and the old URL returns a 404 when V0's preview tries to resolve it.
Fix: Switch to Sonner which is already present in many V0 projects. Run npx shadcn add sonner in your local project if needed, then use toast.success('Submitted!') from 'sonner' in the form submit handler and add <Toaster /> to your root layout.
Replace the toast import with Sonner: add npx shadcn add sonner to your local project, then use toast.success('Submitted!') from 'sonner' in the form submit handler and add <Toaster /> to app/layout.tsxForm validation only triggers on submit — users see all errors at onceWhy: react-hook-form's mode defaults to 'onSubmit'. The useForm configuration in the Form Container does not include a mode option, so no field-level errors appear until the user clicks the Submit Button.
Fix: Add mode: 'onBlur' to the useForm call — useForm({ resolver: zodResolver(schema), mode: 'onBlur' }) — so FormMessage errors appear as users tab between fields.
Add mode: 'onBlur' to the useForm configuration in the Form Container so validation errors appear as the user moves between fields, not only on submit
Server Action receives FormData but the Zod schema expects a typed objectWhy: If V0 wires the form to a Server Action that reads fields with formData.get(), the types don't match the Zod schema's expected object shape. The zodResolver output is a typed plain object, not FormData.
Fix: The Server Action should accept a typed argument directly — not FormData. Remove any formData.get() calls and accept the plain object that react-hook-form's handleSubmit passes. Alternatively, convert at the action boundary: Object.fromEntries(formData).
The Server Action should accept a plain typed object (not FormData) since react-hook-form's handleSubmit serializes values directly; remove formData.get() calls and use the typed argument instead
File upload fails with `body exceeded 4.5MB limit` on VercelWhy: Vercel serverless functions enforce a 4.5MB request body limit. Large files sent through a Server Action will hit this limit and fail with a 413 error.
Fix: For files over 4.5MB, generate a presigned upload URL in a Route Handler, upload directly from the browser to Supabase Storage or Vercel Blob, then pass only the resulting file URL through the Server Action.
For file uploads larger than 4.5MB, generate a Supabase Storage presigned URL in a route handler, upload from the client directly to Supabase, then send only the file URL through the Server Action
Template vs. custom — the honest call
A forked template gets you far, fast. Here's where it holds up, and where you'll outgrow it.
The template is enough when
- Contact form, support request, or lead capture form with email delivery via Resend
- Onboarding questionnaire or user settings page with multiple field types
- Event registration form with basic field validation and confirmation email
- Simple form prototype to test user inputs before building a full backend
Go custom when
- You need a dynamic form builder where end users define their own fields — this requires a schema-per-form database model that the template does not include
- Complex branching logic with 10+ conditional paths based on previous answers
- HIPAA-compliant data collection requiring encryption at rest and audit logging
- E-signature or legally-binding form workflows with document generation
RapidDev adds email delivery, Supabase storage, and an admin submissions view to your V0 form — a complete submission pipeline, usually in 1–2 days.
Frequently asked questions
Is the Next.js Forms template free to use?
Yes. Forking v0 community templates costs nothing. V0 credits are only spent when you type prompts in the chat to customize the template. Design Mode changes to labels and colors are also free.
Can I use this template in a commercial product?
Yes. V0 community templates are available for commercial use. The generated code is yours to deploy in paid SaaS products, client projects, or any commercial application. Review v0.dev's terms of service for the complete licensing details.
Why does the Success Toast break after I fork — 'component not found' error?
V0 references a Toast registry path (`https://ui.shadcn.com/r/styles/new-york-v4/toast.json`) that no longer exists as a standalone shadcn/ui item. The fix is to switch to Sonner: run `npx shadcn add sonner` in your local project and use `toast.success('Submitted!')` from 'sonner'. The first prompt in the gotchas section shows exactly how.
How do I connect the form to a database?
Use the 'Save submissions to Supabase' advanced prompt in this playbook. It creates a form_submissions table with RLS, wires the Server Action to insert rows using the service role key, and adds Cloudflare Turnstile CAPTCHA protection. You can also adapt it to any other database by changing the insert call.
Can I use this form template with the login/auth templates?
Yes. Combine this template with Supabase Auth Starter to build auth-gated forms where submissions are associated with a user ID. After forking both, use the Supabase auth prompt to protect the /form route via middleware and add user_id to the form_submissions table.
Why does my Server Action receive undefined values?
The most common cause is the Server Action trying to read FormData (using formData.get()) instead of accepting the typed object that react-hook-form's handleSubmit passes. Remove the formData.get() calls and accept the plain typed argument directly — react-hook-form serializes form values into a typed object before calling the action.
Can RapidDev extend this form template for my project?
Yes. RapidDev adds email delivery via Resend, Supabase submission storage, and an admin view to your V0 form — delivering a complete submission pipeline, usually in 1–2 days.
Outgrowing the template?
RapidDev turns v0 prototypes into production apps — real auth, database, and payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.