Loading...
Loading...
Zod 4 — TypeScript-first schema validation with static type inference. Use when writing Zod schemas, validating data, defining types with Zod, parsing input, creating form validation schemas, defining API request/response schemas, working with z.object, z.string, z.number, z.enum, z.array, z.union, z.discriminatedUnion, z.file, z.jwt, z.email, z.uuid, z.url, z.codec, z.toJSONSchema, z.fromJSONSchema, z.int, z.stringbool, z.templateLiteral, z.record, z.partialRecord, or any other Zod API. Also use when migrating from Zod 3 to Zod 4, or when the user's package.json shows zod@^4. CRITICAL: Always use Zod 4 APIs. Never use deprecated Zod 3 patterns unless user explicitly requests Zod 3 compatibility.
npx skill4agent add fellipeutaka/leon zodzod@^4.0.0import * as z from "zod";import * as z from "zod/mini";schema.parse(data); // throws ZodError on failure
schema.safeParse(data); // returns { success, data?, error? }
await schema.parseAsync(data);
await schema.safeParseAsync(data);type MyType = z.infer<typeof mySchema>; // output type
type MyInput = z.input<typeof mySchema>; // input type
type MyOutput = z.output<typeof mySchema>; // same as z.inferz.string();
z.number(); // finite numbers only (no Infinity, no NaN)
z.bigint();
z.boolean();
z.symbol();
z.undefined();
z.null();
z.date();
z.nan();z.coerce.string(); // String(input)
z.coerce.number(); // Number(input)
z.coerce.boolean(); // Boolean(input)
z.coerce.bigint(); // BigInt(input)
z.coerce.date(); // new Date(input)z.string().email()// CORRECT (Zod 4)
z.email();
z.uuid();
z.url();
z.httpUrl();
z.hostname();
z.emoji();
z.base64();
z.base64url();
z.hex();
z.jwt();
z.nanoid();
z.cuid();
z.cuid2();
z.ulid();
z.ipv4();
z.ipv6();
z.mac();
z.cidrv4();
z.cidrv6();
z.hash("sha256"); // "sha1" | "sha384" | "sha512" | "md5"
z.e164(); // phone numbers
z.iso.date();
z.iso.time();
z.iso.datetime();
z.iso.duration();
// DEPRECATED (Zod 3 style — do NOT use)
z.string().email(); // ❌
z.string().uuid(); // ❌
z.string().url(); // ❌z.uuid(); // any RFC 9562/4122 UUID
z.uuid({ version: "v4" }); // specific version
z.uuidv4(); // convenience
z.uuidv6();
z.uuidv7();
z.guid(); // any 8-4-4-4-12 hex pattern (less strict)z.email(); // default (Gmail rules)
z.email({ pattern: z.regexes.html5Email }); // browser-style
z.email({ pattern: z.regexes.rfc5322Email }); // RFC 5322
z.email({ pattern: z.regexes.unicodeEmail }); // intl emailsz.jwt();
z.jwt({ alg: "HS256" });z.number(); // any finite number
z.int(); // safe integer range
z.int32(); // int32 range
z.float32(); // float32 range
z.float64(); // float64 range
// bigint variants
z.bigint();
z.int64();
z.uint64();z.number().gt(5);
z.number().gte(5); // alias: .min(5)
z.number().lt(5);
z.number().lte(5); // alias: .max(5)
z.number().positive();
z.number().nonnegative();
z.number().negative();
z.number().nonpositive();
z.number().multipleOf(5); // alias: .step(5)z.string().max(5);
z.string().min(5);
z.string().length(5);
z.string().regex(/pattern/);
z.string().startsWith("abc");
z.string().endsWith("xyz");
z.string().includes("---");
z.string().uppercase();
z.string().lowercase();
z.string().trim();
z.string().toLowerCase();
z.string().toUpperCase();
z.string().normalize();z.object({ name: z.string(), age: z.number() }); // strips unknown keys
z.strictObject({ name: z.string() }); // errors on unknown keys
z.looseObject({ name: z.string() }); // passes unknown keys through.strict().passthrough().strip().merge().deepPartial()schema.extend({ newField: z.string() }); // add fields
schema.pick({ name: true }); // pick fields
schema.omit({ age: true }); // omit fields
schema.partial(); // all optional
schema.partial({ name: true }); // some optional
schema.required(); // all required
schema.keyof(); // ZodEnum from keys
schema.shape.name; // access inner schemaz.object({ ...Base.shape, ...Extra.shape, newField: z.string() });const Category = z.object({
name: z.string(),
get subcategories() {
return z.array(Category);
},
});z.enum(["A", "B", "C"]); // string enum
z.enum(MyTSEnum); // TypeScript enum (replaces z.nativeEnum())
z.enum({ A: 0, B: 1 } as const); // enum-like objectz.nativeEnum()z.enum()z.array(z.string());
z.array(z.string()).min(1).max(10).length(5);
z.tuple([z.string(), z.number()]);
z.tuple([z.string()], z.number()); // variadic: [string, ...number[]]
z.set(z.number());
z.map(z.string(), z.number());z.union([z.string(), z.number()]);
z.discriminatedUnion("status", [
z.object({ status: z.literal("ok"), data: z.string() }),
z.object({ status: z.literal("err"), error: z.string() }),
]);
z.xor([z.string(), z.number()]); // exclusive union (exactly one match)
z.intersection(schemaA, schemaB);z.record(z.string(), z.number()); // Record<string, number>
z.record(z.enum(["a", "b"]), z.string()); // exhaustive: { a: string; b: string }
z.partialRecord(z.enum(["a", "b"]), z.string()); // partial: { a?: string; b?: string }
z.looseRecord(z.string().regex(/^x_/), z.number()); // pass through non-matching keysz.record(z.string())z.literal("hello");
z.literal(42);
z.literal(true);
z.literal(["a", "b", "c"]); // multi-value literal (new in Zod 4)z.file();
z.file().min(10_000); // min size in bytes
z.file().max(1_000_000); // max size in bytes
z.file().mime("image/png"); // single MIME
z.file().mime(["image/png", "image/jpeg"]); // multiple MIMEsz.stringbool(); // "true"/"yes"/"1"/"on"/"y"/"enabled" → true, inverses → false
z.stringbool({ truthy: ["yes", "y"], falsy: ["no", "n"] });
z.stringbool({ case: "sensitive" });z.templateLiteral(["hello, ", z.string()]); // `hello, ${string}`
z.templateLiteral([z.number(), z.enum(["px", "em", "rem"])]); // `${number}px` | ...z.optional(z.string()); // or z.string().optional()
z.nullable(z.string()); // or z.string().nullable()
z.nullish(z.string()); // optional + nullable
z.string().default("hello"); // short-circuits on undefined, returns output type
z.string().prefault("hello"); // pre-parse default, runs through validation
z.number().catch(42); // fallback on any validation error.default().prefault()z.transform((val) => String(val)); // standalone transform
z.string().transform((val) => val.length); // pipe string → transform
z.preprocess((val) => String(val), z.string()); // transform → pipe to schema
z.string().pipe(z.transform((val) => val.length)); // explicit pipe
// .overwrite() — transform without changing inferred type
z.number().overwrite((val) => val ** 2);const stringToDate = z.codec(z.iso.datetime(), z.date(), {
decode: (s) => new Date(s),
encode: (d) => d.toISOString(),
});
stringToDate.decode("2024-01-15T10:30:00.000Z"); // → Date
stringToDate.encode(new Date()); // → string
stringToDate.parse("2024-01-15T10:30:00.000Z"); // → Date (same as decode at runtime)errormessageerrorMapinvalid_type_errorrequired_errorz.string().min(5, { error: "Too short" });
z.string({ error: (issue) => issue.input === undefined ? "Required" : "Not a string" });
z.string({ error: (issue) => {
if (issue.code === "too_small") return `Min ${issue.minimum}`;
}});messageerrorMapinvalid_type_errorrequired_errorz.string().meta({ id: "email", title: "Email", description: "User email" });
z.string().describe("A description"); // shorthand for .meta({ description: ... })
// Custom registries
const myReg = z.registry<{ description: string }>();
z.string().register(myReg, { description: "..." });z.toJSONSchema(schema); // Zod → JSON Schema
z.toJSONSchema(schema, { target: "draft-07" }); // specific draft
z.toJSONSchema(schema, { target: "openapi-3.0" }); // OpenAPI 3.0
z.fromJSONSchema(jsonSchema); // JSON Schema → Zod (experimental)z.string().refine((val) => val.includes("@"), { error: "Must contain @" });
z.string().refine((val) => val.includes("@"), { error: "...", abort: true }); // stop on failure
z.array(z.string()).superRefine((val, ctx) => {
ctx.addIssue({ code: "custom", message: "...", input: val });
});ZodEffects.refine()z.string().refine(v => v.includes("@")).min(5); // works in Zod 4!z.prettifyError(zodError); // formatted multi-line string
z.treeifyError(zodError); // tree structure (replaces .format() and .flatten()).check()z.toJSONSchema()z.fromJSONSchema()when.superRefine().check()