Top Related Projects
Dead simple Object schema validation
A simple and composable way to validate data in JavaScript (and TypeScript).
Runtime type system for IO decoding/encoding
Runtime validation for static types
The fastest JSON schema Validator. Supports JSON Schema draft-04/06/07/2019-09/2020-12 and JSON Type Definition (RFC8927)
Quick Overview
Zod is a TypeScript-first schema declaration and validation library. It allows developers to define schemas for data structures, providing runtime type checking and data validation with static type inference.
Pros
- Strong TypeScript integration with static type inference
- Composable and extensible schema definitions
- Zero dependencies and small bundle size
- Comprehensive validation features including async validation
Cons
- Learning curve for complex schema definitions
- Performance may be slower compared to simpler validation libraries
- Limited built-in support for certain complex data types (e.g., geospatial)
- Primarily focused on TypeScript, less beneficial for pure JavaScript projects
Code Examples
Basic schema definition and validation:
import { z } from "zod";
const userSchema = z.object({
name: z.string(),
age: z.number().min(18),
email: z.string().email(),
});
const result = userSchema.safeParse({
name: "John Doe",
age: 30,
email: "john@example.com",
});
console.log(result.success); // true
Array and nested object validation:
const postSchema = z.object({
title: z.string(),
content: z.string(),
tags: z.array(z.string()),
author: z.object({
id: z.number(),
name: z.string(),
}),
});
const post = {
title: "Hello Zod",
content: "This is a post about Zod",
tags: ["typescript", "validation"],
author: { id: 1, name: "Alice" },
};
const result = postSchema.parse(post);
console.log(result); // Prints the validated post object
Custom validation with refinements:
const passwordSchema = z
.string()
.min(8)
.refine((password) => /[A-Z]/.test(password), {
message: "Password must contain at least one uppercase letter",
})
.refine((password) => /[0-9]/.test(password), {
message: "Password must contain at least one number",
});
const result = passwordSchema.safeParse("weakpwd");
console.log(result.success); // false
console.log(result.error.issues); // Array of validation issues
Getting Started
To start using Zod in your TypeScript project:
-
Install Zod:
npm install zod
-
Import and use Zod in your code:
import { z } from "zod"; const schema = z.string(); const result = schema.safeParse("Hello, Zod!"); console.log(result.success); // true
-
For optimal TypeScript integration, ensure your
tsconfig.json
includes:{ "compilerOptions": { "strict": true, "strictNullChecks": true } }
Competitor Comparisons
Dead simple Object schema validation
Pros of Yup
- More mature and established library with a longer history
- Extensive documentation and community support
- Supports asynchronous validation out of the box
Cons of Yup
- Larger bundle size compared to Zod
- Less TypeScript-friendly, with some type inference limitations
- Slower performance in certain scenarios
Code Comparison
Yup:
import * as Yup from 'yup';
const schema = Yup.object().shape({
name: Yup.string().required(),
age: Yup.number().positive().integer().required(),
});
Zod:
import { z } from 'zod';
const schema = z.object({
name: z.string(),
age: z.number().positive().int(),
});
Both libraries offer similar functionality for defining and validating schemas. Yup uses a chaining API, while Zod leverages TypeScript's type inference for a more concise syntax. Zod's approach results in better type safety and autocompletion in TypeScript environments.
Yup has been around longer and has more extensive documentation, making it easier for beginners to get started. However, Zod's TypeScript-first approach and smaller bundle size make it an attractive option for modern TypeScript projects, especially those prioritizing performance and type safety.
A simple and composable way to validate data in JavaScript (and TypeScript).
Pros of Superstruct
- Simpler API with fewer concepts to learn
- More flexible with custom types and validators
- Smaller bundle size, which can be beneficial for frontend applications
Cons of Superstruct
- Less comprehensive type inference compared to Zod
- Fewer built-in validators and data transformation features
- Not as actively maintained as Zod (fewer recent updates)
Code Comparison
Zod:
const UserSchema = z.object({
name: z.string(),
age: z.number().min(18),
email: z.string().email()
});
Superstruct:
const UserSchema = object({
name: string(),
age: number().min(18),
email: string().pattern(/^[^@]+@[^@]+\.[^@]+$/)
});
Both libraries offer similar functionality for defining schemas and validating data. Zod provides more built-in validators (like .email()
), while Superstruct requires custom patterns for complex validations. Zod's syntax is slightly more concise, but Superstruct's approach may be more intuitive for some developers.
Runtime type system for IO decoding/encoding
Pros of io-ts
- More established project with a longer history and larger community
- Offers advanced features like branded types and refinements
- Integrates well with functional programming paradigms
Cons of io-ts
- Steeper learning curve, especially for developers new to functional programming
- Less intuitive syntax compared to Zod
- Slower runtime performance in some scenarios
Code Comparison
io-ts:
import * as t from 'io-ts'
const User = t.type({
name: t.string,
age: t.number
})
type User = t.TypeOf<typeof User>
Zod:
import { z } from 'zod'
const User = z.object({
name: z.string(),
age: z.number()
})
type User = z.infer<typeof User>
Both libraries provide runtime type checking and static type inference. io-ts uses a more functional approach with its t.type
and t.TypeOf
constructs, while Zod offers a more straightforward, object-oriented syntax. Zod's API is generally considered more intuitive for developers familiar with TypeScript's built-in type system.
While io-ts excels in advanced type manipulation and functional programming patterns, Zod has gained popularity due to its ease of use and performance. The choice between the two often depends on the project's requirements and the team's familiarity with functional programming concepts.
Runtime validation for static types
Pros of Runtypes
- Simpler API with fewer methods, making it easier to learn and use
- Supports recursive types out of the box
- Smaller bundle size, which can be beneficial for frontend applications
Cons of Runtypes
- Less active development and community support compared to Zod
- Fewer built-in validations and transformation options
- Limited documentation and examples available
Code Comparison
Zod:
const UserSchema = z.object({
name: z.string(),
age: z.number().min(18),
email: z.string().email()
});
Runtypes:
const User = Record({
name: String,
age: Number.withConstraint(n => n >= 18),
email: String.withConstraint(s => /@/.test(s))
});
Both libraries provide runtime type checking and validation, but Zod offers more built-in validators (like .email()
), while Runtypes requires custom constraints for complex validations. Zod's API is more extensive, offering additional features like transformations and error customization, whereas Runtypes focuses on a simpler, more straightforward approach to runtime type checking.
The fastest JSON schema Validator. Supports JSON Schema draft-04/06/07/2019-09/2020-12 and JSON Type Definition (RFC8927)
Pros of Ajv
- Higher performance and faster validation, especially for large datasets
- Supports JSON Schema standard, making it more versatile for existing schemas
- More mature project with a larger ecosystem and wider adoption
Cons of Ajv
- More complex API and steeper learning curve
- Less TypeScript-friendly, requiring additional type annotations
- Larger bundle size, which may impact client-side applications
Code Comparison
Ajv:
const Ajv = require("ajv")
const ajv = new Ajv()
const schema = {
type: "object",
properties: {
foo: { type: "integer" },
bar: { type: "string" }
},
required: ["foo"],
additionalProperties: false
}
const validate = ajv.compile(schema)
Zod:
import { z } from "zod"
const schema = z.object({
foo: z.number().int(),
bar: z.string().optional()
})
const result = schema.safeParse(data)
Ajv uses JSON Schema syntax and requires compilation, while Zod offers a more concise and TypeScript-native approach. Zod's API is generally easier to understand and use, especially for TypeScript developers. However, Ajv's performance advantages and JSON Schema compatibility make it a strong choice for certain use cases, particularly when dealing with large datasets or existing JSON Schema definitions.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Zod
TypeScript-first schema validation with static type inference
by @colinhacks
Featured sponsor: Jazz
Read the docs â
What is Zod?
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.
import * as z from "zod/v4";
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);
Features
- Zero external dependencies
- Works in Node.js and all modern browsers
- Tiny:
2kb
core bundle (gzipped) - Immutable API: methods return a new instance
- Concise interface
- Works with TypeScript and plain JS
- Built-in JSON Schema conversion
- Extensive ecosystem
Installation
npm install zod
Basic usage
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.
import * as z from "zod/v4";
const Player = z.object({
username: z.string(),
xp: z.number(),
});
Parsing data
Given any Zod schema, use .parse
to validate an input. If it's valid, Zod returns a strongly-typed deep clone of the input.
Player.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.
const schema = z.string().refine(async (val) => val.length <= 8);
await schema.parseAsync("hello");
// => "hello"
Handling errors
When validation fails, the .parse()
method will throw a ZodError
instance with granular information about the validation issues.
try {
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.
const 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.
const schema = z.string().refine(async (val) => val.length <= 8);
await schema.safeParseAsync("hello");
// => { success: true; data: "hello" }
Inferring types
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.
const 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:
const 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
Top Related Projects
Dead simple Object schema validation
A simple and composable way to validate data in JavaScript (and TypeScript).
Runtime type system for IO decoding/encoding
Runtime validation for static types
The fastest JSON schema Validator. Supports JSON Schema draft-04/06/07/2019-09/2020-12 and JSON Type Definition (RFC8927)
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot