Next.js + Zod Form Validation: Schema Setup Guide
Table of Contents
Next.js form validation with Zod works by defining a z.object() schema for your form fields, connecting it to React Hook Form via zodResolver, and reusing the same schema in Server Actions for server-side validation. One schema covers both client and server.
This guide shows the full setup: schema creation, React Hook Form integration, Server Actions validation, and how to generate schemas quickly from real data.
Why Zod Is the Standard for Next.js Validation
Zod has become the default validation library for Next.js projects for several reasons:
- TypeScript-first: Infers static types automatically — no duplicate type definitions
- Runs on both sides: Same schema validates client form inputs and server action inputs
- tRPC native: tRPC uses Zod schemas for procedure input/output validation
- Official Next.js examples: Next.js docs reference Zod for Server Actions
- React Hook Form support: Official
@hookform/resolverspackage has zodResolver
Step 1: Create the Zod Schema
If you have a sample form submission or API payload, paste it into the JSON to Zod converter to generate a base schema, then add form-specific constraints.
A typical signup form schema:
// lib/schemas/signup.ts
import { z } from 'zod';
export const SignupSchema = z.object({
name: z.string().min(2, 'Name must be at least 2 characters'),
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
confirmPassword: z.string()
}).refine(data => data.password === data.confirmPassword, {
message: 'Passwords do not match',
path: ['confirmPassword']
});
export type SignupFormData = z.infer<typeof SignupSchema>;
Sell Custom Apparel — We Handle Printing & Free Shipping
Step 2: Connect to React Hook Form
Install the resolver:
npm install react-hook-form @hookform/resolvers zod
'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { SignupSchema, SignupFormData } from '@/lib/schemas/signup';
export function SignupForm() {
const { register, handleSubmit, formState: { errors } } = useForm<SignupFormData>({
resolver: zodResolver(SignupSchema)
});
const onSubmit = (data: SignupFormData) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
{errors.email && <p>{errors.email.message}</p>}
</form>
);
}
Step 3: Validate in Server Actions
Reuse the same schema in a Server Action — never trust client-side validation alone:
// app/actions/signup.ts
'use server';
import { SignupSchema } from '@/lib/schemas/signup';
export async function signupAction(formData: FormData) {
const raw = {
name: formData.get('name'),
email: formData.get('email'),
password: formData.get('password'),
confirmPassword: formData.get('confirmPassword')
};
const result = SignupSchema.safeParse(raw);
if (!result.success) {
return { errors: result.error.flatten().fieldErrors };
}
const { name, email, password } = result.data;
// proceed with signup
}
error.flatten().fieldErrors returns an object with field-level error arrays — easy to display next to each input.
Try It Free — No Signup Required
Runs 100% in your browser. No data is collected, stored, or sent anywhere.
Open Free JSON to Zod ConverterFrequently Asked Questions
How do I add Zod validation to a Next.js form?
Install react-hook-form and @hookform/resolvers. Create a z.object() schema, then pass zodResolver(YourSchema) to useForm. Errors appear in formState.errors with field paths.
Can I use the same Zod schema on both client and server in Next.js?
Yes. Define the schema in a shared lib/schemas file and import it in both the React Hook Form component (client) and the Server Action (server). One schema, zero duplication.
How do I validate Server Action inputs with Zod?
Call YourSchema.safeParse(rawFormData) inside the server action. Check result.success. If false, return result.error.flatten().fieldErrors to the client for display.
How do I generate a Zod schema from a JSON sample for my Next.js form?
Paste a sample form submission JSON into the free converter at /developer-tools/json-to-zod/. It outputs the base z.object() schema. Add .min(), .email(), .refine() and other constraints on top.

