Zod
TypeScript-first schema validation with static type inference. Define a schema once and get both runtime validation and TypeScript types. The standard for validating API inputs, forms, and environment variables.
Why Zod?
Validating API request bodies, form inputs, or env vars
You want TypeScript type inference from your validation schema
Using with tRPC, React Hook Form, or Next.js server actions
Signal Breakdown
What drives the Trust Score
Download Trend
Last 12 months
Tradeoffs & Caveats
Know before you commitPython project — use Pydantic instead
Simple one-off validation — native TypeScript types may suffice
Schema size is a concern — Zod bundle is ~13kb gzipped
Pricing
Free tier & paid plans
Open source, free forever
N/A
MIT license
Often Used Together
Complementary tools that pair well with Zod
Learning Resources
Docs, videos, tutorials, and courses
Get Started
Repository and installation options
View on GitHub
github.com/colinhacks/zod
npm install zodQuick Start
Copy and adapt to get going fast
import { z } from 'zod';
// Define schema
const EnvSchema = z.object({
DATABASE_URL: z.string().url(),
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
});
// Validate environment at startup
export const env = EnvSchema.parse(process.env);
// env.PORT is typed as number, env.DATABASE_URL as stringCode Examples
Common usage patterns
API route validation with Next.js
Validate request body in a server action
// app/actions.ts
'use server';
import { z } from 'zod';
const CreatePostSchema = z.object({
title: z.string().min(3).max(200),
content: z.string().min(10),
tags: z.array(z.string()).max(5).default([]),
});
export async function createPost(formData: FormData) {
const raw = Object.fromEntries(formData);
const data = CreatePostSchema.parse(raw);
return db.posts.create({ data });
}Discriminated union
Type-safe result types with Zod
import { z } from 'zod';
const ApiResponse = z.discriminatedUnion('status', [
z.object({ status: z.literal('success'), data: z.array(z.string()) }),
z.object({ status: z.literal('error'), code: z.number(), message: z.string() }),
]);
const response = await fetch('/api/items').then(r => r.json());
const parsed = ApiResponse.parse(response);
if (parsed.status === 'success') {
console.log(parsed.data); // string[]
} else {
console.error(parsed.message); // string
}Community Notes
Real experiences from developers who've used this tool