Zod Cheatsheet
Quick reference for Zod schema validation — primitives, objects, arrays, unions, transforms, and error handling.
Installation & Import
npm install zod
import { z } from 'zod';Primitive Types
z.string()
z.number()
z.boolean()
z.bigint()
z.date()
z.null()
z.undefined()
z.void()
z.any()
z.unknown()
z.never()String Validations
z.string().min(3)
z.string().max(100)
z.string().length(10)
z.string().email()
z.string().url()
z.string().uuid()
z.string().regex(/^[a-z]+$/)
z.string().includes("foo")
z.string().startsWith("http")
z.string().endsWith(".com")
z.string().trim()
z.string().toLowerCase()
z.string().toUpperCase()Number Validations
z.number().min(0)
z.number().max(100)
z.number().int()
z.number().positive()
z.number().negative()
z.number().nonnegative()
z.number().multipleOf(5)
z.number().finite()
z.number().safe() // Number.MIN_SAFE_INTEGER to MAX_SAFE_INTEGERObjects
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email().optional(),
});
// Infer TypeScript type
type User = z.infer<typeof UserSchema>;
// Parse (throws on invalid)
const user = UserSchema.parse(data);
// Safe parse (returns { success, data } or { success, error })
const result = UserSchema.safeParse(data);
if (result.success) console.log(result.data);
// Partial — all fields optional
UserSchema.partial();
// Pick / Omit
UserSchema.pick({ id: true, name: true });
UserSchema.omit({ email: true });
// Extend
UserSchema.extend({ role: z.string() });
// Merge
UserSchema.merge(OtherSchema);Arrays
z.array(z.string())
z.array(z.number()).min(1)
z.array(z.number()).max(10)
z.array(z.number()).length(3)
z.array(z.number()).nonempty()
// Tuple — fixed-length, typed positions
z.tuple([z.string(), z.number()])
z.tuple([z.string(), z.number()]).rest(z.boolean())Unions & Intersections
// Union
z.union([z.string(), z.number()])
// shorthand
z.string().or(z.number())
// Discriminated union (more efficient parsing)
z.discriminatedUnion("type", [
z.object({ type: z.literal("circle"), radius: z.number() }),
z.object({ type: z.literal("square"), side: z.number() }),
]);
// Intersection
z.intersection(SchemaA, SchemaB)
// shorthand
SchemaA.and(SchemaB)
// Enum
z.enum(["admin", "user", "guest"])
z.nativeEnum(MyEnum) // TypeScript enumOptional, Nullable, Default
z.string().optional() // string | undefined
z.string().nullable() // string | null
z.string().nullish() // string | null | undefined
z.string().default("anon") // use default if undefined
z.string().catch("fallback") // use fallback on parse errorTransforms & Refinements
// Transform output
z.string().transform(val => val.toUpperCase())
// Refine with custom validation
z.string().refine(val => val.length > 3, {
message: "Must be longer than 3 characters",
})
// superRefine for multiple issues
z.object({ start: z.date(), end: z.date() }).superRefine((val, ctx) => {
if (val.end < val.start) {
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "end must be after start" });
}
})
// Preprocess input before parsing
z.preprocess(val => Number(val), z.number())Error Handling
const result = schema.safeParse(data);
if (!result.success) {
// Flat error map
console.log(result.error.flatten());
// { fieldErrors: { name: ["Required"] }, formErrors: [] }
// Full error details
result.error.issues.forEach(issue => {
console.log(issue.path, issue.message);
});
}
// Custom error messages
z.string({ required_error: "Name is required" })
z.string().min(3, { message: "At least 3 characters" })Generate a Zod schema from JSON automatically?
Use the JSON to Zod Converter →