zod
SKILL.md
Zod v4 — TypeScript-First Validation
Docs version: Based on Zod v4.x (stable) documentation as of 2025-07-10, including features through v4.2 (codecs, numeric record keys, brand direction). Source: https://zod.dev
You are an expert at data validation using Zod v4. Zod is a TypeScript-first schema declaration and validation library that infers static types from runtime schemas, providing both compile-time safety and runtime validation with zero external dependencies.
Core Principles
- TypeScript-first — Every schema infers a static TypeScript type via
z.infer<typeof schema>. Input and output types can diverge (usez.input<>/z.output<>). - Immutable API — All schema methods return a new instance. Schemas are never mutated in place.
- Parse, don't validate — Use
.parse()(throwsZodError) or.safeParse()(returns{ success, data/error }) to validate data. Parsed results are deep clones. - Refinements live inside schemas — Unlike v3,
.refine()does not wrap schemas inZodEffects. You can freely chain.refine()with.min(),.max(), etc. - Top-level string formats — Use
z.email(),z.uuid(),z.url()etc. instead ofz.string().email()(deprecated). - Unified error param — Use
error(string or function) instead of v3'smessage,invalid_type_error,required_error, orerrorMap. - Requires TypeScript strict mode and TS >= 5.5.
How to Use This Skill
When the user needs Zod help, consult the reference files for detailed patterns:
references/api.md— Complete schema API: primitives, strings, string formats, numbers, objects, arrays, tuples, unions, discriminated unions, records, maps, sets, enums, files, refinements, transforms, pipes, codecs, defaults, branded types, recursive objects, template literals, functionsreferences/errors-and-metadata.md— Error customization (schema-level, per-parse, global), error formatting (z.treeifyError,z.prettifyError,z.flattenError), internationalization/locales, metadata registries (.meta(),z.globalRegistry, custom registries), JSON Schema conversion (z.toJSONSchema,z.fromJSONSchema)references/migration.md— Complete v3-to-v4 migration guide: breaking changes, renamed APIs, updated generics, Zod Mini, library author guidance,zod/v4/corefor dual support
Read the relevant reference file before generating code.
Quick Reference
Installation & Import
npm install zod // v4 is the default
import * as z from "zod"; // full API
import * as z from "zod/mini"; // tree-shakable functional API (1.88kb gzipped core)
Defining Schemas
// Primitives
z.string(); z.number(); z.boolean(); z.bigint();
z.null(); z.undefined(); z.date();
// String formats (top-level, NOT z.string().email())
z.email(); z.uuid(); z.url(); z.ipv4(); z.ipv6();
z.iso.date(); z.iso.datetime(); z.iso.time(); z.iso.duration();
// Numbers & integers
z.int(); z.int32(); z.float32(); z.float64();
// Coercion
z.coerce.string(); z.coerce.number(); z.coerce.boolean();
// Objects
z.object({ name: z.string(), age: z.number() });
z.strictObject({ ... }); // errors on unknown keys
z.looseObject({ ... }); // passes through unknown keys
// Arrays, tuples, unions
z.array(z.string());
z.tuple([z.string(), z.number()]);
z.union([z.string(), z.number()]);
z.discriminatedUnion("status", [ ... ]);
// Records
z.record(z.string(), z.number()); // Record<string, number>
z.partialRecord(z.enum(["a","b"]), z.string()); // optional keys
// Enums (supports TS enums, objects, and string arrays)
z.enum(["A", "B", "C"]);
z.enum(MyTypeScriptEnum); // replaces z.nativeEnum()
// Literals (single or multiple)
z.literal("hello");
z.literal([200, 201, 204]);
Parsing
schema.parse(data); // throws ZodError on failure
schema.safeParse(data); // { success: true, data } | { success: false, error }
await schema.parseAsync(data); // for async refinements/transforms
await schema.safeParseAsync(data);
Type Inference
type User = z.infer<typeof UserSchema>; // output type
type UserInput = z.input<typeof UserSchema>; // input type (may differ with transforms)
Object Operations
Schema.extend({ newField: z.string() });
Schema.pick({ name: true });
Schema.omit({ age: true });
Schema.partial(); // all optional
Schema.partial({ age: true }); // selective
Schema.required();
Schema.keyof(); // ZodEnum from keys
Refinements & Transforms
// Refinements (stay inside schema, don't wrap)
z.string().refine(val => val.includes("@"), { error: "Must contain @" });
z.string().superRefine((val, ctx) => { ctx.addIssue({ ... }); });
// Transforms
z.string().transform(val => val.length); // string -> number
z.string().trim().toLowerCase(); // overwrites (type unchanged)
// Pipes
z.string().pipe(z.coerce.number()).pipe(z.number().min(0));
Error Customization
// Inline error
z.string({ error: "Must be a string" });
z.string().min(5, { error: "Too short" });
// Error function (replaces v3 errorMap, invalid_type_error, required_error)
z.string({ error: (iss) => iss.input === undefined ? "Required" : "Invalid" });
// Per-parse
schema.parse(data, { error: (iss) => "Custom" });
// Global
z.config({ customError: (iss) => "Global custom" });
// Locales
z.config(z.locales.en()); // or fr, de, es, ja, zhCN, etc.
// Formatting
z.prettifyError(error); // human-readable string
z.treeifyError(error); // nested tree structure
z.flattenError(error); // { formErrors, fieldErrors }
JSON Schema
z.toJSONSchema(schema); // default: draft-2020-12
z.toJSONSchema(schema, { target: "draft-07" }); // or "draft-04", "openapi-3.0"
z.toJSONSchema(schema, { io: "input" }); // input type instead of output
z.fromJSONSchema(jsonSchema); // experimental: JSON Schema -> Zod
Metadata & Registries
schema.meta({ title: "Name", description: "User name" }); // adds to z.globalRegistry
schema.meta(); // retrieve metadata
const myRegistry = z.registry<{ description: string }>();
myRegistry.add(schema, { description: "..." });
Recursive Objects (v4 native — no type casting needed)
const Category = z.object({
name: z.string(),
get subcategories() { return z.array(Category); }
});
type Category = z.infer<typeof Category>;
// { name: string; subcategories: Category[] }
Codecs (bidirectional transforms)
const stringToDate = z.codec(z.iso.datetime(), z.date(), {
decode: (s) => new Date(s),
encode: (d) => d.toISOString(),
});
stringToDate.parse("2024-01-15T10:30:00.000Z"); // Date
z.encode(stringToDate, new Date()); // ISO string
Zod Mini (tree-shakable variant)
import * as z from "zod/mini";
z.optional(z.string()); // functional wrappers
z.extend(z.object({ ... }), { age: z.number() });
z.array(z.number()).check(z.minLength(5), z.maxLength(10));
Key v4 Changes (vs v3)
| v3 | v4 |
|---|---|
z.string().email() |
z.email() (top-level) |
z.nativeEnum(E) |
z.enum(E) |
{ message: "..." } |
{ error: "..." } |
z.object({}).strict() |
z.strictObject({}) |
z.object({}).passthrough() |
z.looseObject({}) |
.merge() |
.extend(other.shape) or spread { ...A.shape, ...B.shape } |
z.record(valueSchema) |
z.record(keySchema, valueSchema) (two args required) |
.format() / .flatten() |
z.treeifyError() / z.flattenError() |
._def |
._zod.def |
ZodEffects (wrapping) |
Refinements stored inside schemas |
Performance
- 14x faster string parsing, 7x faster array parsing, 6.5x faster object parsing vs v3
- 100x reduction in
tsctype instantiations - Core bundle: 5.36kb gzipped (regular), 1.88kb gzipped (mini) vs 12.47kb in v3
Weekly Installs
1
Repository
zackbart/skillsFirst Seen
4 days ago
Security Audits
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1