Gathering detailed insights and metrics for zod
Gathering detailed insights and metrics for zod
Gathering detailed insights and metrics for zod
Gathering detailed insights and metrics for zod
TypeScript-first schema validation with static type inference
npm install zod
Typescript
Module System
Node Version
NPM Version
99.3
Supply Chain
100
Quality
95.3
Maintenance
100
Vulnerability
100
License
TypeScript (96.32%)
JavaScript (1.85%)
HTML (1.82%)
Shell (0.01%)
Total Downloads
1,141,599,890
Last Day
1,855,059
Last Week
25,274,135
Last Month
106,183,019
Last Year
745,449,365
MIT License
37,556 Stars
2,150 Commits
1,409 Forks
70 Watchers
62 Branches
335 Contributors
Updated on May 06, 2025
Minified
Minified + Gzipped
Latest Version
3.24.4
Package Id
zod@3.24.4
Unpacked Size
682.55 kB
Size
119.47 kB
File Count
52
NPM Version
10.8.2
Node Version
20.19.1
Published on
May 04, 2025
Cumulative downloads
Total Downloads
Last Day
9.3%
1,855,059
Compared to previous day
Last Week
-1.3%
25,274,135
Compared to previous week
Last Month
16.6%
106,183,019
Compared to previous month
Last Year
142.9%
745,449,365
Compared to previous year
37
zod.dev
TypeScript-first schema validation with static type inference
Zod 4 is now in beta!
|
Zod is a TypeScript-first schema declaration and validation library. I'm using the term "schema" to broadly refer to any data type, from a simple string
to a complex nested object.
Zod is designed to be as developer-friendly as possible. The goal is to eliminate duplicative type declarations. With Zod, you declare a validator once and Zod will automatically infer the static TypeScript type. It's easy to compose simpler types into complex data structures.
Some other great aspects:
.optional()
) return a new instanceSponsorship at any level is appreciated and encouraged. If you built a paid product using Zod, consider one of the corporate tiers.
|
|
|
|
|
|
|
Nitric |
PropelAuth |
Cerbos |
Scalar |
Trigger.dev |
Transloadit |
Infisical |
Whop |
CryptoJobsList |
Plain. |
Inngest |
Storyblok |
Mux |
Cybozu |
|
|
|
|
![]() |
|
|
|
|
|
There are a growing number of tools that are built atop or support Zod natively! If you've built a tool or library on top of Zod, tell me about it on Twitter or start a Discussion. I'll add it below and tweet it out.
tRPC
: Build end-to-end typesafe APIs without GraphQL.@anatine/zod-nestjs
: Helper methods for using Zod in a NestJS project.zod-endpoints
: Contract-first strictly typed endpoints with Zod. OpenAPI compatible.zhttp
: An OpenAPI compatible, strictly typed http library with Zod input and response validation.domain-functions
: Decouple your business logic from your framework using composable functions. With first-class type inference from end to end powered by Zod schemas.@zodios/core
: A typescript API client with runtime and compile time validation backed by axios and zod.express-zod-api
: Build Express-based APIs with I/O schema validation and custom middlewares.tapiduck
: End-to-end typesafe JSON APIs with Zod and Express; a bit like tRPC, but simpler.koa-zod-router
: Create typesafe routes in Koa with I/O validation using Zod.zod-sockets
: Zod-powered Socket.IO microframework with I/O validation and built-in AsyncAPI specsoas-tszod-gen
: Client SDK code generator to convert OpenApi v3 specifications into TS endpoint caller functions with Zod types.GQLoom
: Weave GraphQL schema and resolvers using Zod.oRPC
: Typesafe APIs Made Simplereact-hook-form
: A first-party Zod resolver for React Hook Form.TanStack Form
: Headless, performant, and type-safe form state management for TS/JS, React, Vue, Angular, Solid, and Litzod-validation-error
: Generate user-friendly error messages from ZodError
s.zod-formik-adapter
: A community-maintained Formik adapter for Zod.react-zorm
: Standalone <form>
generation and validation for React using Zod.zodix
: Zod utilities for FormData and URLSearchParams in Remix loaders and actions.conform
: A typesafe form validation library for progressive enhancement of HTML forms. Works with Remix and Next.js.remix-params-helper
: Simplify integration of Zod with standard URLSearchParams and FormData for Remix apps.formik-validator-zod
: Formik-compliant validator library that simplifies using Zod with Formik.zod-i18n-map
: Useful for translating Zod error messages.@modular-forms/solid
: Modular form library for SolidJS that supports Zod for validation.houseform
: A React form library that uses Zod for validation.sveltekit-superforms
: Supercharged form library for SvelteKit with Zod validation.mobx-zod-form
: Data-first form builder based on MobX & Zod.@vee-validate/zod
: Form library for Vue.js with Zod schema validation.zod-form-renderer
: Auto-infer form fields from zod schema and render them with react-hook-form with E2E type safety.antd-zod
: Zod adapter for Ant Design form fields validation.frrm
: Tiny 0.5kb Zod-based, HTML form abstraction that goes brr.zod-to-ts
: Generate TypeScript definitions from Zod schemas.zod-to-json-schema
: Convert your Zod schemas into JSON Schemas.@anatine/zod-openapi
: Converts a Zod schema to an OpenAPI v3.x SchemaObject
.zod-fast-check
: Generate fast-check
arbitraries from Zod schemas.zod-dto
: Generate Nest.js DTOs from a Zod schema.fastify-type-provider-zod
: Create Fastify type providers from Zod schemas.zod-to-openapi
: Generate full OpenAPI (Swagger) docs from Zod, including schemas, endpoints & parameters.nestjs-graphql-zod
: Generates NestJS GraphQL model classes from Zod schemas. Provides GraphQL method decorators working with Zod schemas.zod-openapi
: Create full OpenAPI v3.x documentation from Zod schemas.fastify-zod-openapi
: Fastify type provider, validation, serialization and @fastify/swagger support for Zod schemas.typeschema
: Universal adapter for schema validation.zodex
: (De)serialization for zod schemasts-to-zod
: Convert TypeScript definitions into Zod schemas.@runtyping/zod
: Generate Zod from static types & JSON schema.json-schema-to-zod
: Convert your JSON Schemas into Zod schemas. Live demo.json-to-zod
: Convert JSON objects into Zod schemas. Live demo.graphql-codegen-typescript-validation-schema
: GraphQL Code Generator plugin to generate form validation schema from your GraphQL schema.zod-prisma
: Generate Zod schemas from your Prisma schema.Supervillain
: Generate Zod schemas from your Go structs.prisma-zod-generator
: Emit Zod schemas from your Prisma schema.drizzle-zod
: Emit Zod schemas from your Drizzle schema.prisma-trpc-generator
: Emit fully implemented tRPC routers and their validation schemas using Zod.zod-prisma-types
Create Zod types from your Prisma models.quicktype
: Convert JSON objects and JSON schemas into Zod schemas.@sanity-typed/zod
: Generate Zod Schemas from Sanity Schemas.java-to-zod
: Convert POJOs to Zod schemasOrval
: Generate Zod schemas from OpenAPI schemasKubb
: Generate SDKs and Zod schemas from your OpenAPI schemas@anatine/zod-mock
: Generate mock data from a Zod schema. Powered by faker.js.zod-mocking
: Generate mock data from your Zod schemas.zod-fixture
: Use your zod schemas to automate the generation of non-relevant test fixtures in a deterministic way.zocker
: Generate plausible mock-data from your schemas.zodock
Generate mock data based on Zod schemas.zod-schema-faker
Generates mock data from Zod schemas. Powered by @faker-js/faker and randexp.jsfreerstore
: Firestore cost optimizer.slonik
: Node.js Postgres client with strong Zod integration.schemql
: Enhances your SQL workflow by combining raw SQL with targeted type safety and schema validation.soly
: Create CLI applications with zod.pastel
: Create CLI applications with react, zod, and ink.zod-xlsx
: A xlsx based resource validator using Zod schemas.znv
: Type-safe environment parsing and validation for Node.js with Zod schemas.zod-config
: Load configurations across multiple sources with flexible adapters, ensuring type safety with Zod.unplugin-environment
: A plugin for loading enviroment variables safely with schema validation, simple with virtual module, type-safe with intellisense, and better DX 🔥 🚀 👷. Powered by Zod.zod-struct
: Create runtime-checked structs with Zod.zod-csv
: Validation helpers for zod for parsing CSV data.fullproduct.dev
: Universal Expo + Next.js App Starter that uses Zod schemas as the single source of truth to keep generated MDX docs, GraphQL, database models, forms, and fetcher functions in sync.zod_utilz
: Framework agnostic utilities for Zod.zod-playground
: A tool for learning and testing Zod schema validation functionalities. Link.zod-sandbox
: Controlled environment for testing zod schemas. Live demo.zod-dev
: Conditionally disables Zod runtime parsing in production.zod-accelerator
: Accelerates Zod's throughput up to ~100x.TypeScript 4.5+!
You must enable strict
mode in your tsconfig.json
. This is a best practice for all TypeScript projects.
1// tsconfig.json 2{ 3 // ... 4 "compilerOptions": { 5 // ... 6 "strict": true 7 } 8}
npm
1npm install zod # npm 2deno add npm:zod # deno 3yarn add zod # yarn 4bun add zod # bun 5pnpm add zod # pnpm
Zod also publishes a canary version on every commit. To install the canary:
1npm install zod@canary # npm 2deno add npm:zod@canary # deno 3yarn add zod@canary # yarn 4bun add zod@canary # bun 5pnpm add zod@canary # pnpm
The rest of this README assumes you are using npm and importing directly from the
"zod"
package.
Creating a simple string schema
1import { z } from "zod"; 2 3// creating a schema for strings 4const mySchema = z.string(); 5 6// parsing 7mySchema.parse("tuna"); // => "tuna" 8mySchema.parse(12); // => throws ZodError 9 10// "safe" parsing (doesn't throw error if validation fails) 11mySchema.safeParse("tuna"); // => { success: true; data: "tuna" } 12mySchema.safeParse(12); // => { success: false; error: ZodError }
Creating an object schema
1import { z } from "zod"; 2 3const User = z.object({ 4 username: z.string(), 5}); 6 7User.parse({ username: "Ludwig" }); 8 9// extract the inferred type 10type User = z.infer<typeof User>; 11// { username: string }
1import { z } from "zod"; 2 3// primitive values 4z.string(); 5z.number(); 6z.bigint(); 7z.boolean(); 8z.date(); 9z.symbol(); 10 11// empty types 12z.undefined(); 13z.null(); 14z.void(); // accepts undefined 15 16// catch-all types 17// allows any value 18z.any(); 19z.unknown(); 20 21// never type 22// allows no values 23z.never();
Zod now provides a more convenient way to coerce primitive values.
1const schema = z.coerce.string(); 2schema.parse("tuna"); // => "tuna" 3schema.parse(12); // => "12"
During the parsing step, the input is passed through the String()
function, which is a JavaScript built-in for coercing data into strings.
1schema.parse(12); // => "12" 2schema.parse(true); // => "true" 3schema.parse(undefined); // => "undefined" 4schema.parse(null); // => "null"
The returned schema is a normal ZodString
instance so you can use all string methods.
1z.coerce.string().email().min(5);
How coercion works
All primitive types support coercion. Zod coerces all inputs using the built-in constructors: String(input)
, Number(input)
, new Date(input)
, etc.
1z.coerce.string(); // String(input) 2z.coerce.number(); // Number(input) 3z.coerce.boolean(); // Boolean(input) 4z.coerce.bigint(); // BigInt(input) 5z.coerce.date(); // new Date(input)
Note — Boolean coercion with z.coerce.boolean()
may not work how you expect. Any truthy value is coerced to true
, and any falsy value is coerced to false
.
1const schema = z.coerce.boolean(); // Boolean(input) 2 3schema.parse("tuna"); // => true 4schema.parse("true"); // => true 5schema.parse("false"); // => true 6schema.parse(1); // => true 7schema.parse([]); // => true 8 9schema.parse(0); // => false 10schema.parse(""); // => false 11schema.parse(undefined); // => false 12schema.parse(null); // => false
For more control over coercion logic, consider using z.preprocess
or z.pipe()
.
Literal schemas represent a literal type, like "hello world"
or 5
.
1const tuna = z.literal("tuna"); 2const twelve = z.literal(12); 3const twobig = z.literal(2n); // bigint literal 4const tru = z.literal(true); 5 6const terrificSymbol = Symbol("terrific"); 7const terrific = z.literal(terrificSymbol); 8 9// retrieve literal value 10tuna.value; // "tuna"
Currently there is no support for Date literals in Zod. If you have a use case for this feature, please file an issue.
Zod includes a handful of string-specific validations.
1// validations 2z.string().max(5); 3z.string().min(5); 4z.string().length(5); 5z.string().email(); 6z.string().url(); 7z.string().emoji(); 8z.string().uuid(); 9z.string().nanoid(); 10z.string().cuid(); 11z.string().cuid2(); 12z.string().ulid(); 13z.string().regex(regex); 14z.string().includes(string); 15z.string().startsWith(string); 16z.string().endsWith(string); 17z.string().datetime(); // ISO 8601; by default only `Z` timezone allowed 18z.string().ip(); // defaults to allow both IPv4 and IPv6 19z.string().cidr(); // defaults to allow both IPv4 and IPv6 20 21// transforms 22z.string().trim(); // trim whitespace 23z.string().toLowerCase(); // toLowerCase 24z.string().toUpperCase(); // toUpperCase 25 26// added in Zod 3.23 27z.string().date(); // ISO date format (YYYY-MM-DD) 28z.string().time(); // ISO time format (HH:mm:ss[.SSSSSS] or HH:mm) 29z.string().duration(); // ISO 8601 duration 30z.string().base64();
Check out validator.js for a bunch of other useful string validation functions that can be used in conjunction with Refinements.
You can customize some common error messages when creating a string schema.
1const name = z.string({ 2 required_error: "Name is required", 3 invalid_type_error: "Name must be a string", 4});
When using validation methods, you can pass in an additional argument to provide a custom error message.
1z.string().min(5, { message: "Must be 5 or more characters long" }); 2z.string().max(5, { message: "Must be 5 or fewer characters long" }); 3z.string().length(5, { message: "Must be exactly 5 characters long" }); 4z.string().email({ message: "Invalid email address" }); 5z.string().url({ message: "Invalid url" }); 6z.string().emoji({ message: "Contains non-emoji characters" }); 7z.string().uuid({ message: "Invalid UUID" }); 8z.string().includes("tuna", { message: "Must include tuna" }); 9z.string().startsWith("https://", { message: "Must provide secure URL" }); 10z.string().endsWith(".com", { message: "Only .com domains allowed" }); 11z.string().datetime({ message: "Invalid datetime string! Must be UTC." }); 12z.string().date({ message: "Invalid date string!" }); 13z.string().time({ message: "Invalid time string!" }); 14z.string().ip({ message: "Invalid IP address" }); 15z.string().cidr({ message: "Invalid CIDR" });
As you may have noticed, Zod string includes a few date/time related validations. These validations are regular expression based, so they are not as strict as a full date/time library. However, they are very convenient for validating user input.
The z.string().datetime()
method enforces ISO 8601; default is no timezone offsets and arbitrary sub-second decimal precision. Seconds may be omitted if precision is not set.
1const datetime = z.string().datetime(); 2 3datetime.parse("2020-01-01T00:00:00Z"); // pass 4datetime.parse("2020-01-01T00:00:00.123Z"); // pass 5datetime.parse("2020-01-01T00:00:00.123456Z"); // pass (arbitrary precision) 6datetime.parse("2020-01-01T00:00Z"); // pass (hours and minutes only) 7datetime.parse("2020-01-01T00:00:00+02:00"); // fail (no offsets allowed)
Timezone offsets can be allowed by setting the offset
option to true
.
1const datetime = z.string().datetime({ offset: true }); 2 3datetime.parse("2020-01-01T00:00:00+02:00"); // pass 4datetime.parse("2020-01-01T00:00+02:00"); // pass 5datetime.parse("2020-01-01T00:00:00.123+02:00"); // pass (millis optional) 6datetime.parse("2020-01-01T00:00:00.123+0200"); // pass (millis optional) 7datetime.parse("2020-01-01T00:00:00.123+02"); // pass (only offset hours) 8datetime.parse("2020-01-01T00:00:00Z"); // pass (Z still supported)
Allow unqualified (timezone-less) datetimes with the local
flag.
1const schema = z.string().datetime({ local: true }); 2schema.parse("2020-01-01T00:00:00"); // pass 3schema.parse("2020-01-01T00:00"); // pass
You can additionally constrain the allowable precision
. By default, arbitrary sub-second precision is supported (but optional).
1const datetime = z.string().datetime({ precision: 3 }); 2 3datetime.parse("2020-01-01T00:00:00.123Z"); // pass 4datetime.parse("2020-01-01T00:00:00Z"); // fail 5datetime.parse("2020-01-01T00:00Z"); // fail 6datetime.parse("2020-01-01T00:00:00.123456Z"); // fail
Added in Zod 3.23
The z.string().date()
method validates strings in the format YYYY-MM-DD
.
1const date = z.string().date(); 2 3date.parse("2020-01-01"); // pass 4date.parse("2020-1-1"); // fail 5date.parse("2020-01-32"); // fail
Added in Zod 3.23
The z.string().time()
method validates strings in the format HH:MM
or HH:MM:SS[.s+]
. The second can include arbitrary decimal precision. It does not allow timezone offsets of any kind.
1const time = z.string().time(); 2 3time.parse("00:00:00"); // pass 4time.parse("09:52:31"); // pass 5time.parse("09:52"); // pass 6time.parse("23:59:59.9999999"); // pass (arbitrary precision) 7 8time.parse("00:00:00.123Z"); // fail (no `Z` allowed) 9time.parse("00:00:00.123+02:00"); // fail (no offsets allowed)
You can set the precision
option to constrain the allowable decimal precision.
1const time = z.string().time({ precision: 3 }); 2 3time.parse("00:00:00.123"); // pass 4time.parse("00:00:00.123456"); // fail 5time.parse("00:00:00"); // fail 6time.parse("00:00"); // fail
By default .ip()
allows both IPv4 and IPv6.
1const ip = z.string().ip(); 2 3ip.parse("192.168.1.1"); // pass 4ip.parse("84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003"); // pass 5ip.parse("84d5:51a0:9114:1855:4cfa:f2d7:1f12:192.168.1.1"); // pass 6 7ip.parse("256.1.1.1"); // fail 8ip.parse("84d5:51a0:9114:gggg:4cfa:f2d7:1f12:7003"); // fail
You can additionally set the IP version
.
1const ipv4 = z.string().ip({ version: "v4" }); 2ipv4.parse("84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003"); // fail 3 4const ipv6 = z.string().ip({ version: "v6" }); 5ipv6.parse("192.168.1.1"); // fail
Validate IP address ranges specified with CIDR notation. By default, .cidr()
allows both IPv4 and IPv6.
1const cidr = z.string().cidr(); 2cidr.parse("192.168.0.0/24"); // pass 3cidr.parse("2001:db8::/32"); // pass
You can specify a version with the version
parameter.
1const ipv4Cidr = z.string().cidr({ version: "v4" }); 2ipv4Cidr.parse("84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003"); // fail 3 4const ipv6Cidr = z.string().cidr({ version: "v6" }); 5ipv6Cidr.parse("192.168.1.1"); // fail
You can customize certain error messages when creating a number schema.
1const age = z.number({ 2 required_error: "Age is required", 3 invalid_type_error: "Age must be a number", 4});
Zod includes a handful of number-specific validations.
1z.number().gt(5); 2z.number().gte(5); // alias .min(5) 3z.number().lt(5); 4z.number().lte(5); // alias .max(5) 5 6z.number().int(); // value must be an integer 7 8z.number().positive(); // > 0 9z.number().nonnegative(); // >= 0 10z.number().negative(); // < 0 11z.number().nonpositive(); // <= 0 12 13z.number().multipleOf(5); // Evenly divisible by 5. Alias .step(5) 14 15z.number().finite(); // value must be finite, not Infinity or -Infinity 16z.number().safe(); // value must be between Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER
Optionally, you can pass in a second argument to provide a custom error message.
1z.number().lte(5, { message: "this👏is👏too👏big" });
Zod includes a handful of bigint-specific validations.
1z.bigint().gt(5n); 2z.bigint().gte(5n); // alias `.min(5n)` 3z.bigint().lt(5n); 4z.bigint().lte(5n); // alias `.max(5n)` 5 6z.bigint().positive(); // > 0n 7z.bigint().nonnegative(); // >= 0n 8z.bigint().negative(); // < 0n 9z.bigint().nonpositive(); // <= 0n 10 11z.bigint().multipleOf(5n); // Evenly divisible by 5n.
You can customize certain error messages when creating a nan schema.
1const isNaN = z.nan({ 2 required_error: "isNaN is required", 3 invalid_type_error: "isNaN must be 'not a number'", 4});
You can customize certain error messages when creating a boolean schema.
1const isActive = z.boolean({ 2 required_error: "isActive is required", 3 invalid_type_error: "isActive must be a boolean", 4});
Use z.date() to validate Date
instances.
1z.date().safeParse(new Date()); // success: true 2z.date().safeParse("2022-01-12T00:00:00.000Z"); // success: false
You can customize certain error messages when creating a date schema.
1const myDateSchema = z.date({ 2 required_error: "Please select a date and time", 3 invalid_type_error: "That's not a date!", 4});
Zod provides a handful of date-specific validations.
1z.date().min(new Date("1900-01-01"), { message: "Too old" }); 2z.date().max(new Date(), { message: "Too young!" });
Coercion to Date
Since zod 3.20, use z.coerce.date()
to pass the input through new Date(input)
.
1const dateSchema = z.coerce.date();
2type DateSchema = z.infer<typeof dateSchema>;
3// type DateSchema = Date
4
5/* valid dates */
6console.log(dateSchema.safeParse("2023-01-10T00:00:00.000Z").success); // true
7console.log(dateSchema.safeParse("2023-01-10").success); // true
8console.log(dateSchema.safeParse("1/10/23").success); // true
9console.log(dateSchema.safeParse(new Date("1/10/23")).success); // true
10
11/* invalid dates */
12console.log(dateSchema.safeParse("2023-13-10").success); // false
13console.log(dateSchema.safeParse("0000-00-00").success); // false
For older zod versions, use z.preprocess
like described in this thread.
1const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]); 2type FishEnum = z.infer<typeof FishEnum>; 3// 'Salmon' | 'Tuna' | 'Trout'
z.enum
is a Zod-native way to declare a schema with a fixed set of allowable string values. Pass the array of values directly into z.enum()
. Alternatively, use as const
to define your enum values as a tuple of strings. See the const assertion docs for details.
1const VALUES = ["Salmon", "Tuna", "Trout"] as const; 2const FishEnum = z.enum(VALUES);
This is not allowed, since Zod isn't able to infer the exact values of each element.
1const fish = ["Salmon", "Tuna", "Trout"]; 2const FishEnum = z.enum(fish);
.enum
To get autocompletion with a Zod enum, use the .enum
property of your schema:
1FishEnum.enum.Salmon; // => autocompletes 2 3FishEnum.enum; 4/* 5=> { 6 Salmon: "Salmon", 7 Tuna: "Tuna", 8 Trout: "Trout", 9} 10*/
You can also retrieve the list of options as a tuple with the .options
property:
1FishEnum.options; // ["Salmon", "Tuna", "Trout"];
.exclude/.extract()
You can create subsets of a Zod enum with the .exclude
and .extract
methods.
1const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]); 2const SalmonAndTrout = FishEnum.extract(["Salmon", "Trout"]); 3const TunaOnly = FishEnum.exclude(["Salmon", "Trout"]);
Zod enums are the recommended approach to defining and validating enums. But if you need to validate against an enum from a third-party library (or you don't want to rewrite your existing enums) you can use z.nativeEnum()
.
Numeric enums
1enum Fruits {
2 Apple,
3 Banana,
4}
5
6const FruitEnum = z.nativeEnum(Fruits);
7type FruitEnum = z.infer<typeof FruitEnum>; // Fruits
8
9FruitEnum.parse(Fruits.Apple); // passes
10FruitEnum.parse(Fruits.Banana); // passes
11FruitEnum.parse(0); // passes
12FruitEnum.parse(1); // passes
13FruitEnum.parse(3); // fails
String enums
1enum Fruits {
2 Apple = "apple",
3 Banana = "banana",
4 Cantaloupe, // you can mix numerical and string enums
5}
6
7const FruitEnum = z.nativeEnum(Fruits);
8type FruitEnum = z.infer<typeof FruitEnum>; // Fruits
9
10FruitEnum.parse(Fruits.Apple); // passes
11FruitEnum.parse(Fruits.Cantaloupe); // passes
12FruitEnum.parse("apple"); // passes
13FruitEnum.parse("banana"); // passes
14FruitEnum.parse(0); // passes
15FruitEnum.parse("Cantaloupe"); // fails
Const enums
The .nativeEnum()
function works for as const
objects as well. ⚠️ as const
requires TypeScript 3.4+!
1const Fruits = {
2 Apple: "apple",
3 Banana: "banana",
4 Cantaloupe: 3,
5} as const;
6
7const FruitEnum = z.nativeEnum(Fruits);
8type FruitEnum = z.infer<typeof FruitEnum>; // "apple" | "banana" | 3
9
10FruitEnum.parse("apple"); // passes
11FruitEnum.parse("banana"); // passes
12FruitEnum.parse(3); // passes
13FruitEnum.parse("Cantaloupe"); // fails
You can access the underlying object with the .enum
property:
1FruitEnum.enum.Apple; // "apple"
You can make any schema optional with z.optional()
. This wraps the schema in a ZodOptional
instance and returns the result.
1const schema = z.optional(z.string()); 2 3schema.parse(undefined); // => returns undefined 4type A = z.infer<typeof schema>; // string | undefined
For convenience, you can also call the .optional()
method on an existing schema.
1const user = z.object({ 2 username: z.string().optional(), 3}); 4type C = z.infer<typeof user>; // { username?: string | undefined };
You can extract the wrapped schema from a ZodOptional
instance with .unwrap()
.
1const stringSchema = z.string(); 2const optionalString = stringSchema.optional(); 3optionalString.unwrap() === stringSchema; // true
Similarly, you can create nullable types with z.nullable()
.
1const nullableString = z.nullable(z.string()); 2nullableString.parse("asdf"); // => "asdf" 3nullableString.parse(null); // => null
Or use the .nullable()
method.
1const E = z.string().nullable(); // equivalent to nullableString 2type E = z.infer<typeof E>; // string | null
Extract the inner schema with .unwrap()
.
1const stringSchema = z.string(); 2const nullableString = stringSchema.nullable(); 3nullableString.unwrap() === stringSchema; // true
1// all properties are required by default 2const Dog = z.object({ 3 name: z.string(), 4 age: z.number(), 5}); 6 7// extract the inferred type like this 8type Dog = z.infer<typeof Dog>; 9 10// equivalent to: 11type Dog = { 12 name: string; 13 age: number; 14};
.shape
Use .shape
to access the schemas for a particular key.
1Dog.shape.name; // => string schema
2Dog.shape.age; // => number schema
.keyof
Use .keyof
to create a ZodEnum
schema from the keys of an object schema.
1const keySchema = Dog.keyof(); 2keySchema; // ZodEnum<["name", "age"]>
.extend
You can add additional fields to an object schema with the .extend
method.
1const DogWithBreed = Dog.extend({ 2 breed: z.string(), 3});
You can use .extend
to overwrite fields! Be careful with this power!
.merge
Equivalent to A.extend(B.shape)
.
1const BaseTeacher = z.object({ students: z.array(z.string()) }); 2const HasID = z.object({ id: z.string() }); 3 4const Teacher = BaseTeacher.merge(HasID); 5type Teacher = z.infer<typeof Teacher>; // => { students: string[], id: string }
If the two schemas share keys, the properties of B overrides the property of A. The returned schema also inherits the "unknownKeys" policy (strip/strict/passthrough) and the catchall schema of B.
.pick/.omit
Inspired by TypeScript's built-in Pick
and Omit
utility types, all Zod object schemas have .pick
and .omit
methods that return a modified version. Consider this Recipe schema:
1const Recipe = z.object({ 2 id: z.string(), 3 name: z.string(), 4 ingredients: z.array(z.string()), 5});
To only keep certain keys, use .pick
.
1const JustTheName = Recipe.pick({ name: true }); 2type JustTheName = z.infer<typeof JustTheName>; 3// => { name: string }
To remove certain keys, use .omit
.
1const NoIDRecipe = Recipe.omit({ id: true }); 2 3type NoIDRecipe = z.infer<typeof NoIDRecipe>; 4// => { name: string, ingredients: string[] }
.partial
Inspired by the built-in TypeScript utility type Partial, the .partial
method makes all properties optional.
Starting from this object:
1const user = z.object({ 2 email: z.string(), 3 username: z.string(), 4}); 5// { email: string; username: string }
We can create a partial version:
1const partialUser = user.partial(); 2// { email?: string | undefined; username?: string | undefined }
You can also specify which properties to make optional:
1const optionalEmail = user.partial({ 2 email: true, 3}); 4/* 5{ 6 email?: string | undefined; 7 username: string 8} 9*/
.deepPartial
The .partial
method is shallow — it only applies one level deep. There is also a "deep" version:
1const user = z.object({ 2 username: z.string(), 3 location: z.object({ 4 latitude: z.number(), 5 longitude: z.number(), 6 }), 7 strings: z.array(z.object({ value: z.string() })), 8}); 9 10const deepPartialUser = user.deepPartial(); 11 12/* 13{ 14 username?: string | undefined, 15 location?: { 16 latitude?: number | undefined; 17 longitude?: number | undefined; 18 } | undefined, 19 strings?: { value?: string}[] 20} 21*/
Important limitation: deep partials only work as expected in hierarchies of objects, arrays, and tuples.
.required
Contrary to the .partial
method, the .required
method makes all properties required.
Starting from this object:
1const user = z 2 .object({ 3 email: z.string(), 4 username: z.string(), 5 }) 6 .partial(); 7// { email?: string | undefined; username?: string | undefined }
We can create a required version:
1const requiredUser = user.required(); 2// { email: string; username: string }
You can also specify which properties to make required:
1const requiredEmail = user.required({ 2 email: true, 3}); 4/* 5{ 6 email: string; 7 username?: string | undefined; 8} 9*/
.passthrough
By default Zod object schemas strip out unrecognized keys during parsing.
1const person = z.object({ 2 name: z.string(), 3}); 4 5person.parse({ 6 name: "bob dylan", 7 extraKey: 61, 8}); 9// => { name: "bob dylan" } 10// extraKey has been stripped
Instead, if you want to pass through unknown keys, use .passthrough()
.
1person.passthrough().parse({ 2 name: "bob dylan", 3 extraKey: 61, 4}); 5// => { name: "bob dylan", extraKey: 61 }
.strict
By default Zod object schemas strip out unrecognized keys during parsing. You can disallow unknown keys with .strict()
. If there are any unknown keys in the input, Zod will throw an error.
1const person = z 2 .object({ 3 name: z.string(), 4 }) 5 .strict(); 6 7person.parse({ 8 name: "bob dylan", 9 extraKey: 61, 10}); 11// => throws ZodError
.strip
You can use the .strip
method to reset an object schema to the default behavior (stripping unrecognized keys).
.catchall
You can pass a "catchall" schema into an object schema. All unknown keys will be validated against it.
1const person = z 2 .object({ 3 name: z.string(), 4 }) 5 .catchall(z.number()); 6 7person.parse({ 8 name: "bob dylan", 9 validExtraKey: 61, // works fine 10}); 11 12person.parse({ 13 name: "bob dylan", 14 validExtraKey: false, // fails 15}); 16// => throws ZodError
Using .catchall()
obviates .passthrough()
, .strip()
, or .strict()
. All keys are now considered "known".
1const stringArray = z.array(z.string()); 2 3// equivalent 4const stringArray = z.string().array();
Be careful with the .array()
method. It returns a new ZodArray
instance. This means the order in which you call methods matters. For instance:
1z.string().optional().array(); // (string | undefined)[] 2z.string().array().optional(); // string[] | undefined
.element
Use .element
to access the schema for an element of the array.
1stringArray.element; // => string schema
.nonempty
If you want to ensure that an array contains at least one element, use .nonempty()
.
1const nonEmptyStrings = z.string().array().nonempty(); 2// the inferred type is now 3// [string, ...string[]] 4 5nonEmptyStrings.parse([]); // throws: "Array cannot be empty" 6nonEmptyStrings.parse(["Ariana Grande"]); // passes
You can optionally specify a custom error message:
1// optional custom error message 2const nonEmptyStrings = z.string().array().nonempty({ 3 message: "Can't be empty!", 4});
.min/.max/.length
1z.string().array().min(5); // must contain 5 or more items 2z.string().array().max(5); // must contain 5 or fewer items 3z.string().array().length(5); // must contain 5 items exactly
Unlike .nonempty()
these methods do not change the inferred type.
Unlike arrays, tuples have a fixed number of elements and each element can have a different type.
1const athleteSchema = z.tuple([ 2 z.string(), // name 3 z.number(), // jersey number 4 z.object({ 5 pointsScored: z.number(), 6 }), // statistics 7]); 8 9type Athlete = z.infer<typeof athleteSchema>; 10// type Athlete = [string, number, { pointsScored: number }]
A variadic ("rest") argument can be added with the .rest
method.
1const variadicTuple = z.tuple([z.string()]).rest(z.number()); 2const result = variadicTuple.parse(["hello", 1, 2, 3]); 3// => [string, ...number[]];
Zod includes a built-in z.union
method for composing "OR" types.
1const stringOrNumber = z.union([z.string(), z.number()]); 2 3stringOrNumber.parse("foo"); // passes 4stringOrNumber.parse(14); // passes
Zod will test the input against each of the "options" in order and return the first value that validates successfully.
For convenience, you can also use the .or
method:
1const stringOrNumber = z.string().or(z.number());
Optional string validation:
To validate an optional form input, you can union the desired string validation with an empty string literal.
This example validates an input that is optional but needs to contain a valid URL:
1const optionalUrl = z.union([z.string().url().nullish(), z.literal("")]); 2 3console.log(optionalUrl.safeParse(undefined).success); // true 4console.log(optionalUrl.safeParse(null).success); // true 5console.log(optionalUrl.safeParse("").success); // true 6console.log(optionalUrl.safeParse("https://zod.dev").success); // true 7console.log(optionalUrl.safeParse("not a valid url").success); // false
A discriminated union is a union of object schemas that all share a particular key.
1type MyUnion = 2 | { status: "success"; data: string } 3 | { status: "failed"; error: Error };
Such unions can be represented with the z.discriminatedUnion
method. This enables faster evaluation, because Zod can check the discriminator key (status
in the example above) to determine which schema should be used to parse the input. This makes parsing more efficient and lets Zod report friendlier errors.
With the basic union method, the input is tested against each of the provided "options", and in the case of invalidity, issues for all the "options" are shown in the zod error. On the other hand, the discriminated union allows for selecting just one of the "options", testing against it, and showing only the issues related to this "option".
1const myUnion = z.discriminatedUnion("status", [ 2 z.object({ status: z.literal("success"), data: z.string() }), 3 z.object({ status: z.literal("failed"), error: z.instanceof(Error) }), 4]); 5 6myUnion.parse({ status: "success", data: "yippie ki yay" });
You can extract a reference to the array of schemas with the .options
property.
1myUnion.options; // [ZodObject<...>, ZodObject<...>]
To merge two or more discriminated unions, use .options
with destructuring.
1const A = z.discriminatedUnion("status", [ 2 /* options */ 3]); 4const B = z.discriminatedUnion("status", [ 5 /* options */ 6]); 7 8const AB = z.discriminatedUnion("status", [...A.options, ...B.options]);
Record schemas are used to validate types such as Record<string, number>
. This is particularly useful for storing or caching items by ID.
1const User = z.object({ name: z.string() }); 2 3const UserStore = z.record(z.string(), User); 4type UserStore = z.infer<typeof UserStore>; 5// => Record<string, { name: string }>
The schema and inferred type can be used like so:
1const userStore: UserStore = {}; 2 3userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = { 4 name: "Carlotta", 5}; // passes 6 7userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = { 8 whatever: "Ice cream sundae", 9}; // TypeError
A note on numerical keys
While z.record(keyType, valueType)
is able to accept numerical key types and TypeScript's built-in Record type is Record<KeyType, ValueType>
, it's hard to represent the TypeScript type Record<number, any>
in Zod.
As it turns out, TypeScript's behavior surrounding [k: number]
is a little unintuitive:
1const testMap: { [k: number]: string } = { 2 1: "one", 3}; 4 5for (const key in testMap) { 6 console.log(`${key}: ${typeof key}`); 7} 8// prints: `1: string`
As you can see, JavaScript automatically casts all object keys to strings under the hood. Since Zod is trying to bridge the gap between static and runtime types, it doesn't make sense to provide a way of creating a record schema with numerical keys, since there's no such thing as a numerical key in runtime JavaScript.
1const stringNumberMap = z.map(z.string(), z.number()); 2 3type StringNumberMap = z.infer<typeof stringNumberMap>; 4// type StringNumberMap = Map<string, number>
1const numberSet = z.set(z.number()); 2type NumberSet = z.infer<typeof numberSet>; 3// type NumberSet = Set<number>
Set schemas can be further constrained with the following utility methods.
1z.set(z.string()).nonempty(); // must contain at least one item 2z.set(z.string()).min(5); // must contain 5 or more items 3z.set(z.string()).max(5); // must contain 5 or fewer items 4z.set(z.string()).size(5); // must contain 5 items exactly
Intersections are useful for creating "logical AND" types. This is useful for intersecting two object types.
1const Person = z.object({ 2 name: z.string(), 3}); 4 5const Employee = z.object({ 6 role: z.string(), 7}); 8 9const EmployedPerson = z.intersection(Person, Employee); 10 11// equivalent to: 12const EmployedPerson = Person.and(Employee);
Though in many cases, it is recommended to use A.merge(B)
to merge two objects. The .merge
method returns a new ZodObject
instance, whereas A.and(B)
returns a less useful ZodIntersection
instance that lacks common object methods like pick
and omit
.
1const a = z.union([z.number(), z.string()]); 2const b = z.union([z.number(), z.boolean()]); 3const c = z.intersection(a, b); 4 5type c = z.infer<typeof c>; // => number
You can define a recursive schema in Zod, but because of a limitation of TypeScript, their type can't be statically inferred. Instead you'll need to define the type definition manually, and provide it to Zod as a "type hint".
1const baseCategorySchema = z.object({ 2 name: z.string(), 3}); 4 5type Category = z.infer<typeof baseCategorySchema> & { 6 subcategories: Category[]; 7}; 8 9const categorySchema: z.ZodType<Category> = baseCategorySchema.extend({ 10 subcategories: z.lazy(() => categorySchema.array()), 11}); 12 13categorySchema.parse({ 14 name: "People", 15 subcategories: [ 16 { 17 name: "Politicians", 18 subcategories: [ 19 { 20 name: "Presidents", 21 subcategories: [], 22 }, 23 ], 24 }, 25 ], 26}); // passes
Thanks to crasite for this example.
When using z.ZodType
with z.ZodEffects
(
.refine
,
.transform
,
preprocess
,
etc...
), you will need to define the input and output types of the schema. z.ZodType<Output, z.ZodTypeDef, Input>
1const isValidId = (id: string): id is `${string}/${string}` => 2 id.split("/").length === 2; 3 4const baseSchema = z.object({ 5 id: z.string().refine(isValidId), 6}); 7 8type Input = z.input<typeof baseSchema> & { 9 children: Input[]; 10}; 11 12type Output = z.output<typeof baseSchema> & { 13 children: Output[]; 14}; 15 16const schema: z.ZodType<Output, z.ZodTypeDef, Input> = baseSchema.extend({ 17 children: z.lazy(() => schema.array()), 18});
Thanks to marcus13371337 and JoelBeeldi for this example.
If you want to validate any JSON value, you can use the snippet below.
1const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]); 2type Literal = z.infer<typeof literalSchema>; 3type Json = Literal | { [
5.3/10
Summary
Zod denial of service vulnerability
Affected Versions
<= 3.22.2
Patched Versions
3.22.3
Reason
no dangerous workflow patterns detected
Reason
30 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 10/30 approved changesets -- score normalized to 3
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
31 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-04-28
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More