Loaded automatically when its description matches the active task. Read only the section you need, then follow the link to the relevant reference file for full detail.
Use this skill when
Defining runtime-validated schemas for API request/response bodies, env vars, or form data
Using .parse() or .safeParse() and handling ZodError with per-field issue paths
Composing schemas with .extend(), .merge(), .pick(), .omit(), .partial(), .required()
Writing refinements (.refine(), .superRefine()) or transforms (.transform(), .pipe())
Modeling state machines or tagged unions with z.discriminatedUnion()
Extracting static TypeScript types with z.infer<typeof schema>
Validating environment variables with z.coerce and providing typed env objects
Building recursive or self-referential schemas with z.lazy()
Generating JSON Schema / OpenAPI specs from Zod schemas via zod-to-json-schema
Integrating Zod with React Hook Form via @hookform/resolvers/zod
Migrating from Zod 3 to Zod 4 (breaking changes: .email() removed, .extend behavior, perf)
Creating branded/opaque types for domain primitives (UserId, Email, etc.)
Do not use this skill when
Task is about yup or joi — suggest migrating to Zod; don't maintain those APIs
Task is tRPC router input/output typing and tRPC is an active skill in the project (→trpc)
Task is pure TypeScript type-system design (mapped types, conditional types, generics) with no runtime validation — use typescript
Task is JSON Schema validation without TypeScript (AJV, pure JSON Schema) — Zod is TS-first
Task is Pydantic (Python equivalent) — different runtime, different skill
Purpose
Zod is the dominant TypeScript-first runtime validation library — the gap-bridger between TypeScript's compile-time type system and the untrusted data at runtime (HTTP requests, env vars, user input, localStorage). Unlike type assertions, Zod validates at runtime, narrows types automatically, and generates human-readable errors with per-field issue paths.
Zod 4 (the current major) ships significantly faster parsing performance, a redesigned z.string() API (.email() removed in favor of z.email()), updated .extend() semantics, and first-class z.file() support. This skill covers the full Zod surface: primitives, composition, transforms, async validation, error handling, z.lazy recursion, branded types, and integrations with React Hook Form and zod-to-json-schema. It owns the validation layer — the framework skill (fastify, hono, nextjs) owns how the schema is wired into the request lifecycle.
.parse(data) throws ZodError on failure. .safeParse(data) returns { success: true, data } or { success: false, error: ZodError } — preferred for request handlers. .parseAsync() / .safeParseAsync() for schemas with async refinements. ZodError.issues is an array of ZodIssue objects with path, code, message. Flatten to a field-keyed object with error.flatten().
.transform(fn) converts parsed value to a new type — changes the output type. .refine(fn, message) validates without changing type. .superRefine(fn) for multiple issues or conditional logic. .pipe(schema) chains schemas (useful after transform). Async refinements: .refineAsync(async fn) — requires .parseAsync(). Preprocess inputs with z.preprocess(fn, schema) (coerce before parsing).
z.union([A, B, C]) tries each schema in order — use for small sets. z.discriminatedUnion("type", [A, B]) uses a literal discriminator field for O(1) dispatch — always prefer this for tagged unions / state machines. z.intersection(A, B) combines two schemas (all fields required to pass both). Literal union shorthand: z.enum(["a", "b"]) generates a TS "a" | "b" type.
z.coerce.string(), z.coerce.number(), z.coerce.boolean(), z.coerce.date() — calls the constructor on the value before parsing. Essential for env vars (strings → numbers). .default(value) or .default(() => value) supplies a fallback when value is undefined. .catch(value) returns fallback instead of throwing on any error. .optional() → T | undefined; .nullable() → T | null; .nullish() → T | undefined | null.
z.lazy(() => schema) enables self-referential schemas (tree nodes, categories). Pair with a TS interface and z.ZodType<T> annotation to avoid inference loops. Branded types: schema.brand<"BrandName">() produces z.infer output that carries the brand tag — prevents mixing UserId and PostId at the type level. Unwrap with z.infer<typeof schema>.
@hookform/resolvers/zod bridges Zod and React Hook Form. Pass zodResolver(schema) as the resolver option to useForm. Types flow automatically: useForm<z.infer<typeof schema>>(). Errors from ZodError surface in formState.errors keyed by field path. Works with nested objects and arrays.
zodToJsonSchema(schema) converts a Zod schema to a JSON Schema draft-7/2019-09 object. Used for OpenAPI spec generation, Claude tool definitions, and JSON Schema validators. Pass options: { target: "openApi3", $refStrategy: "none" }. Discriminated unions become oneOf. Brands and transforms are stripped.
Wrong vs right — parse vs safeParse at boundaries, Zod 3→4 syntax, union vs discriminatedUnion, raw env vs validated, preprocess vs transform, strip vs strict, z.any()