name: zod-v4 description: > Zod v4 validation library reference. Auto-loads when working with Zod schemas, validation, z.object, z.string, z.infer, safeParse, transforms, discriminatedUnion. user-invocable: false
Zod v4 Reference
Version: 4.x Docs: https://zod.dev
See reference.md for the full API (imports, types, parsing, schema manipulation, error handling, common patterns).
Zod 4 Breaking Changes from 3.x
-
Records with enum keys are exhaustive - All keys required by default
// v4: produces { a: number; b: number } (both required) z.record(z.enum(['a', 'b']), z.number()) // For optional keys, use z.partialRecord() z.partialRecord(z.enum(['a', 'b']), z.number()) // { a?: number; b?: number } -
Error customization unified -
message,invalid_type_error,required_errorremoved// v3 z.string({ required_error: 'Required', invalid_type_error: 'Must be string' }) // v4 z.string({ error: (iss) => iss.input === undefined ? 'Required' : 'Must be string' }) -
Default behavior -
.default()must match output type (use.prefault()for pre-parse defaults) -
ZodError.errors removed - Use
ZodError.issuesinstead
Key Gotchas
Optional vs Nullable vs Nullish
| Method | Type | Use When |
|---|---|---|
.optional() | T | undefined | Field can be absent |
.nullable() | T | null | Field uses null as empty |
.nullish() | T | null | undefined | Messy external data |
Don't chain .optional().nullable() — use .nullish()
z.interface() for Precise Optionality (v4)
z.interface({ "name?": z.string() }) // { name?: string } — key may be omitted
z.interface({ name: z.string().optional() }) // { name: string | undefined } — key required
Performance
- Zod 4 is 14x faster for strings, 7x for arrays, 6.5x for objects vs v3
- Use
z.discriminatedUnion()overz.union()for object schemas - Cache parsed results — don't parse the same data repeatedly
Anti-Patterns
- Don't use
.any()instead of.unknown()—.unknown()forces type narrowing - Don't parse the same data repeatedly — Parse once at boundaries
- Don't ignore
.safeParse()errors — Always checkresult.success - Don't use refinements for transformations — Use
.transform()for conversion,.refine()for validation - Don't throw in refinement functions — Return
false, Zod handles errors - Don't nest
.safeParse()calls — Compose schemas instead - Don't use regular unions for discriminated data — Use
z.discriminatedUnion() - Don't mix sync and async schemas — If any part is async, use async parsing
- Don't cast after validation — Derive types with
z.infer<typeof schema> - Don't maintain separate types and schemas — Schema-first, derive types
- Don't chain
.optional().nullable()— Use.nullish()