Zod
Complete reference for Zod — the TypeScript-first schema validation library with static type inference
Installation & Setup
Install Zod and start defining schemas
Install Zod and import it into your project
# Install Zod
npm install zod
bun add zod
pnpm add zod
// import.ts
import * as z from "zod";
const schema = z.string();
schema.parse("hello"); // "hello"Primitive Schemas
Built-in schemas for JavaScript primitive types
Schemas for strings, numbers, booleans, dates, and special types
import * as z from "zod";
z.string();
z.number();
z.boolean();
z.date();
z.bigint();
z.symbol();
z.null();
z.undefined();
z.any();
z.unknown();
z.never();String Validation
String-specific validators and format checks
Length, format, and pattern validators for strings
z.string().min(3);
z.string().max(20);
z.string().length(10);
z.email();
z.url();
z.uuid();
z.string().regex(/^[a-z]+$/);Number Validation
Number-specific validators for ranges and integer constraints
Range, integer, and sign validators for numbers
z.number().min(0);
z.number().max(100);
z.number().int();
z.number().positive();
z.number().negative();
z.number().multipleOf(5);Object Schemas
Define and manipulate object shapes with z.object()
Create object schemas with required and optional properties
const UserSchema = z.object({
id: z.uuid(),
name: z.string(),
email: z.email(),
age: z.number().optional(),
createdAt: z.date(),
});
type User = z.infer<typeof UserSchema>;Transform existing object schemas with extend, pick, omit, partial
const UserSchema = z.object({
id: z.uuid(),
name: z.string(),
email: z.email(),
});
const PublicUser = UserSchema.omit({ email: true });
const UserUpdate = UserSchema.partial();
const AdminUser = UserSchema.extend({ role: z.string() });Arrays, Tuples, Sets & Maps
Schemas for collection types
Define arrays, tuples, sets, maps, and records
// Array
z.array(z.string());
z.string().array();
// Tuple (fixed length, mixed types)
z.tuple([z.string(), z.number(), z.boolean()]);
// Record (key-value object)
z.record(z.string(), z.number());Unions, Intersections & Discriminated Unions
Combine multiple schemas with unions and intersections
Union, intersection, and discriminated union patterns
// Union (any of)
const StringOrNumber = z.union([z.string(), z.number()]);
// or: z.string().or(z.number())
// Discriminated union (faster, better errors)
const Shape = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("circle"), radius: z.number() }),
z.object({ kind: z.literal("square"), size: z.number() }),
]);Enums & Literals
Define schemas for fixed sets of values
Restrict values to a known set with z.enum() and z.literal()
// Literal (exact value)
const Tuna = z.literal("tuna");
// String enum
const Fish = z.enum(["tuna", "salmon", "trout"]);
// Native TypeScript enum
enum Status { ACTIVE = "ACTIVE", INACTIVE = "INACTIVE" }
const StatusSchema = z.nativeEnum(Status);Optional, Nullable, Default & Catch
Handle missing values, defaults, and fallbacks
Optional, nullable, default values, and catch fallbacks
z.string().optional(); // string | undefined
z.string().nullable(); // string | null
z.string().nullish(); // string | null | undefined
z.string().default("hello");
z.string().catch("fallback");Parsing & Error Handling
Parse data and handle validation errors
Throwing and non-throwing parse methods
// parse - throws on error
try {
const user = UserSchema.parse(data);
} catch (err) {
if (err instanceof z.ZodError) {
console.log(err.issues);
}
}
// safeParse - returns result object
const result = UserSchema.safeParse(data);
if (result.success) {
console.log(result.data);
} else {
console.log(result.error.issues);
}Set custom error messages on schemas and validators
z.string({ message: "Name is required" });
z.string().min(3, "Too short");
z.email({ message: "Invalid email" });
// Per-validator
z.string()
.min(3, "At least 3 characters")
.max(20, "At most 20 characters")
.regex(/^[a-z]+$/, "Lowercase only");Refinements & Transforms
Custom validation logic and data transformations
Add custom validation logic to any schema
// Simple refinement
const PositiveInt = z.number().refine(
(val) => Number.isInteger(val) && val > 0,
{ message: "Must be a positive integer" }
);
// Object-level refinement
const Form = z.object({
password: z.string().min(8),
confirm: z.string(),
}).refine((data) => data.password === data.confirm, {
message: "Passwords don't match",
path: ["confirm"],
});Convert and transform data during validation
// Transform output
z.string().transform((val) => val.toUpperCase());
// Chain with pipe
z.string()
.transform((val) => parseInt(val))
.pipe(z.number().int().positive());
// Coerce input to type
z.coerce.number().parse("42"); // 42
z.coerce.date().parse("2024-01-01");Type Inference
Extract TypeScript types from Zod schemas
Use schemas as the single source of truth for runtime and types
const UserSchema = z.object({
name: z.string(),
age: z.number(),
});
type User = z.infer<typeof UserSchema>;
// { name: string; age: number }