TypeScript-first schema declaration and validation library with static type inference
275M
15K
0
Install
npm i zodGitHub-flavored package documentation.
0
5.5 MB
MIT
Aug 29, 2026
Zod is a TypeScript-first validation library. Define a schema and parse some data with it. You'll get back a strongly typed, validated result.
tscodeimport * as z from "zod"; const User = z.object({ name: z.string(), }); // some untrusted data... const input = { /* stuff */ }; // the parsed result is validated and type safe! const data = User.parse(input); // so you can use it with confidence :) console.log(data.name);
2kb core bundle (gzipped)shcodenpm install zod
Before you can do anything else, you need to define a schema. For the purposes of this guide, we'll use a simple object schema.
tscodeimport * as z from "zod"; const Player = z.object({ username: z.string(), xp: z.number(), });
Given any Zod schema, use .parse to validate an input. If it's valid, Zod returns a strongly-typed deep clone of the input.
tscodePlayer.parse({ username: "billie", xp: 100 }); // => returns { username: "billie", xp: 100 }
Note — If your schema uses certain asynchronous APIs like async refinements or transforms, you'll need to use the .parseAsync() method instead.
tscodeconst schema = z.string().refine(async (val) => val.length <= 8); await schema.parseAsync("hello"); // => "hello"
For hot validation paths, z.compile(schema) returns a schema clone with an ahead-of-time compiled fast path. Valid inputs take the compiled path; invalid inputs fall back to the regular parser so error reporting stays identical.
Across a 55-schema benchmark the median speedup is 2.4x, and it scales with how much work the schema does per parse: a large array of objects is ~9x, a 20-key object ~9x, a nested object ~4.5x, while a bare z.string() gains nothing — compilation removes per-node dispatch and allocation, and a single typeof has none to remove.
tscodeconst CompiledPlayer = z.compile(Player); CompiledPlayer.parse({ username: "billie", xp: 100 });
To enable compilation globally for schemas constructed after import:
tscodeimport "zod/compile"; // place before modules that define schemas
Things to know:
new Function. Global mode is automatically disabled when z.config({ jitless: true }) is set (e.g. CSP environments); calling z.compile() directly is an explicit opt-in.z.compile() hands the schema back unchanged and it keeps using the regular parser, exactly as global mode leaves it. Pass { strict: true } to throw ZodCompileAsyncError / ZodCompileUnsupportedError instead..refine(), .extend(), etc.) returns an uncompiled schema — compile the final schema.See compile docs for details.
When validation fails, the .parse() method will throw a ZodError instance with granular information about the validation issues.
tscodetry { Player.parse({ username: 42, xp: "100" }); } catch (err) { if (err instanceof z.ZodError) { err.issues; /* [ { expected: 'string', code: 'invalid_type', path: [ 'username' ], message: 'Invalid input: expected string' }, { expected: 'number', code: 'invalid_type', path: [ 'xp' ], message: 'Invalid input: expected number' } ] */ } }
To avoid a try/catch block, you can use the .safeParse() method to get back a plain result object containing either the successfully parsed data or a ZodError. The result type is a discriminated union, so you can handle both cases conveniently.
tscodeconst result = Player.safeParse({ username: 42, xp: "100" }); if (!result.success) { result.error; // ZodError instance } else { result.data; // { username: string; xp: number } }
Note — If your schema uses certain asynchronous APIs like async refinements or transforms, you'll need to use the .safeParseAsync() method instead.
tscodeconst schema = z.string().refine(async (val) => val.length <= 8); await schema.safeParseAsync("hello"); // => { success: true; data: "hello" }
Zod infers a static type from your schema definitions. You can extract this type with the z.infer<> utility and use it however you like.
tscodeconst Player = z.object({ username: z.string(), xp: z.number(), }); // extract the inferred type type Player = z.infer<typeof Player>; // use it in your code const player: Player = { username: "billie", xp: 100 };
In some cases, the input & output types of a schema can diverge. For instance, the .transform() API can convert the input from one type to another. In these cases, you can extract the input and output types independently:
tscodeconst mySchema = z.string().transform((val) => val.length); type MySchemaIn = z.input<typeof mySchema>; // => string type MySchemaOut = z.output<typeof mySchema>; // equivalent to z.infer<typeof mySchema> // number
4.5.3
4.5.2
4.5.0-canary.20260825T051321
4.5.0-canary.20260828T163622
4.5.0
4.5.1