# Zod Next.js Integration > Validate Next.js server actions, API routes, and form data with Zod schemas ## When to Use - Validating inputs to Next.js server actions (App Router) - Parsing and validating request bodies in API route handlers - Handling form data validation with `useFormState` / `useActionState` - Validating search params and dynamic route segments ## Instructions 1. Validate server action inputs — the action receives `FormData` or typed arguments: ```typescript 'use server'; import { z } from 'zod'; import { redirect } from 'next/navigation'; const CreatePostSchema = z.object({ title: z.string().min(1, 'Title is required').max(100), content: z.string().min(10, 'Content is too short'), published: z.coerce.boolean().default(false), }); export async function createPost(formData: FormData) { const result = CreatePostSchema.safeParse({ title: formData.get('title'), content: formData.get('content'), published: formData.get('published'), }); if (!result.success) { return { success: false, errors: result.error.flatten().fieldErrors }; } const post = await db.post.create({ data: result.data }); redirect(`/posts/${post.id}`); } ``` 2. Use `useActionState` (React 19) or `useFormState` with typed state: ```typescript 'use client' import { useActionState } from 'react' import { createPost } from './actions' type FormState = { success: boolean errors?: { title?: string[]; content?: string[] } } const initialState: FormState = { success: false } export function CreatePostForm() { const [state, formAction, isPending] = useActionState(createPost, initialState) return (
) } ``` 3. Validate API route handler inputs in the App Router: ```typescript import { z } from 'zod'; import { NextRequest, NextResponse } from 'next/server'; const CreateUserSchema = z.object({ name: z.string().min(1), email: z.string().email(), role: z.enum(['admin', 'viewer']).default('viewer'), }); export async function POST(request: NextRequest) { const body = await request.json().catch(() => null); if (!body) { return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 }); } const result = CreateUserSchema.safeParse(body); if (!result.success) { return NextResponse.json( { success: false, errors: result.error.flatten().fieldErrors }, { status: 422 } ); } const user = await db.user.create({ data: result.data }); return NextResponse.json({ success: true, user }, { status: 201 }); } ``` 4. Validate search params: ```typescript import { z } from 'zod'; const SearchParamsSchema = z.object({ page: z.coerce.number().int().positive().default(1), limit: z.coerce.number().int().min(1).max(100).default(20), query: z.string().optional(), sort: z.enum(['asc', 'desc']).default('desc'), }); // In a Server Component: export default function Page({ searchParams }: { searchParams: Record