zod vs ajv vs joi vs typanion vs yup
Data Validation Libraries
zodajvjoitypanionyupSimilar Packages:

Data Validation Libraries

Data validation libraries in JavaScript are tools that help ensure the data being processed in an application meets specific criteria or rules. These libraries are essential for validating user input, API requests, and any data that needs to conform to a particular structure or format. They provide a way to define validation schemas, check data against these schemas, and handle errors when the data is invalid. This process helps improve data integrity, enhance security by preventing malicious input, and provide better feedback to users or developers when data does not meet the expected standards. Popular data validation libraries include ajv, joi, yup, zod, and typanion, each with its unique features and use cases.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
zod264,439,48143,9666.14 MB644 days agoMIT
ajv014,8321.03 MB3785 months agoMIT
joi021,1731.89 MB2006 days agoBSD-3-Clause
typanion0274133 kB193 years agoMIT
yup023,665270 kB253a year agoMIT

Feature Comparison: zod vs ajv vs joi vs typanion vs yup

TypeScript Support

  • zod:

    zod is a TypeScript-first library that provides the best type inference among these options. It is designed to work seamlessly with TypeScript, making it ideal for projects that prioritize type safety and inference.

  • ajv:

    ajv provides TypeScript definitions, but its primary focus is on JSON Schema validation rather than TypeScript type inference. It is more suited for projects where JSON Schema compliance is critical.

  • joi:

    joi has good TypeScript support, but it was originally designed for JavaScript. Recent versions have improved type definitions, making it more type-safe and easier to use in TypeScript projects.

  • typanion:

    typanion is designed with TypeScript in mind, offering first-class support for TypeScript types and type safety. It leverages TypeScript's type system to provide more accurate type inference and validation.

  • yup:

    yup offers excellent TypeScript support, with well-defined types and interfaces that make it easy to use in TypeScript projects. It provides good type inference for schemas and validation functions.

Asynchronous Validation

  • zod:

    zod supports asynchronous validation, particularly for validating data with async functions or promises. It allows for flexible integration of async validation logic.

  • ajv:

    ajv supports asynchronous validation out of the box, especially for validating data against schemas that include asynchronous constraints or custom keywords.

  • joi:

    joi supports asynchronous validation, allowing validators to return promises. This is useful for scenarios like validating data against external sources or performing async checks.

  • typanion:

    typanion supports asynchronous validation, making it suitable for scenarios where validation requires async operations, such as checking values against a database or an API.

  • yup:

    yup has built-in support for asynchronous validation, allowing you to define async validation rules and handle promises in your validation logic.

Error Handling

  • zod:

    zod offers structured error handling with detailed error messages. It provides a clear API for accessing validation errors, making it easy to integrate with UI frameworks and handle errors effectively.

  • ajv:

    ajv provides detailed error messages and supports custom error formatting. It allows you to access validation errors in a structured way, making it easier to handle and display them.

  • joi:

    joi offers comprehensive error handling with detailed messages. It provides a rich error object that includes information about the validation failure, making it easy to customize error handling and messaging.

  • typanion:

    typanion provides structured error handling that integrates with TypeScript's type system. It allows for clear and consistent error reporting, making it easier to handle validation errors in a type-safe manner.

  • yup:

    yup provides clear and customizable error messages. It allows you to access validation errors in a structured format, making it easy to display errors in user interfaces or handle them programmatically.

Schema Definition

  • zod:

    zod uses a declarative, TypeScript-first approach for schema definition. It is designed to be simple and intuitive, with a focus on type safety and minimal boilerplate code.

  • ajv:

    ajv uses JSON Schema for schema definition, which is a standardized format for describing the structure of JSON data. This makes it highly interoperable and suitable for projects that require strict schema compliance.

  • joi:

    joi uses a fluent API for schema definition, allowing for expressive and readable validation rules. It is highly customizable and supports complex nested validations, making it versatile for various use cases.

  • typanion:

    typanion uses a TypeScript-based approach for schema definition, leveraging TypeScript's type system to enforce validation rules. This makes it particularly suited for TypeScript projects where type safety is a priority.

  • yup:

    yup uses a fluent API similar to joi for defining schemas. It is designed to be simple and intuitive, making it easy to create both simple and complex validation rules.

Ease of Use: Code Examples

  • zod:

    Zod Validation Example

    import { z } from 'zod';
    const schema = z.object({
      name: z.string(),
      age: z.number().min(0)
    });
    const result = schema.safeParse({ name: 'John', age: 30 });
    if (!result.success) {
      console.error(result.error.format());
    } else {
      console.log('Valid:', result.data);
    }
    
  • ajv:

    JSON Schema Validation with ajv

    const Ajv = require('ajv');
    const ajv = new Ajv();
    const validate = ajv.compile({
      type: 'object',
      properties: {
        name: { type: 'string' },
        age: { type: 'integer', minimum: 0 }
      },
      required: ['name', 'age'],
      additionalProperties: false
    });
    const valid = validate({ name: 'John', age: 30 });
    if (!valid) console.log(validate.errors);
    
  • joi:

    Object Validation with joi

    const Joi = require('joi');
    const schema = Joi.object({
      name: Joi.string().required(),
      age: Joi.number().integer().min(0).required()
    });
    const { error, value } = schema.validate({ name: 'John', age: 30 });
    if (error) console.log(error.details);
    
  • typanion:

    Type-Safe Validation with typanion

    import { createValidator } from 'typanion';
    const validate = createValidator({
      name: (value) => typeof value === 'string',
      age: (value) => typeof value === 'number' && value >= 0
    });
    const isValid = validate({ name: 'John', age: 30 });
    if (!isValid) console.error('Validation failed');
    
  • yup:

    Yup Validation Example

    const yup = require('yup');
    const schema = yup.object().shape({
      name: yup.string().required(),
      age: yup.number().integer().min(0).required()
    });
    schema.validate({ name: 'John', age: 30 })
      .then((value) => console.log('Valid:', value))
      .catch((err) => console.log('Error:', err.errors));
    

How to Choose: zod vs ajv vs joi vs typanion vs yup

  • zod:

    Choose zod if you prefer a TypeScript-first schema declaration and validation library that provides static type inference. It is designed to be simple and intuitive, with a focus on type safety and minimal boilerplate, making it a great choice for modern TypeScript projects.

  • ajv:

    Choose ajv if you need a fast, standards-compliant JSON Schema validator that supports asynchronous validation and custom keywords. It is ideal for projects that require strict adherence to JSON Schema specifications and high performance.

  • joi:

    Select joi if you want a powerful and expressive schema description language for validating JavaScript objects. It offers a rich set of built-in validators, supports asynchronous validation, and is highly customizable, making it suitable for complex validation scenarios.

  • typanion:

    Opt for typanion if you are looking for a TypeScript-first validation library that leverages TypeScript's type system to provide type-safe validation. It is designed for developers who want to enforce types and validation rules in a way that integrates seamlessly with TypeScript's type checking.

  • yup:

    Use yup if you need a schema builder for runtime value parsing and validation, especially in React applications. It is inspired by joi but is more lightweight and has a simpler API, making it easy to use for both simple and complex validations.

README for zod

Zod logo

Zod

TypeScript-first schema validation with static type inference
by @colinhacks


Zod CI status License npm discord server stars

Docs   •   Discord   •   𝕏   •   Bluesky


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";

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";

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"

AOT compilation

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.

const CompiledPlayer = z.compile(Player);

CompiledPlayer.parse({ username: "billie", xp: 100 });

To enable compilation globally for schemas constructed after import:

import "zod/compile"; // place before modules that define schemas

Things to know:

  • Compilation uses 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.
  • Schemas with async refinements or transforms can't be compiled, and neither can a few other constructs. That is not an error: 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.
  • On invalid input, refinements and transforms may run twice (fast path, then fallback).
  • Deriving a new schema from a compiled one (.refine(), .extend(), etc.) returns an uncompiled schema — compile the final schema.

See compile docs for details.

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, received number'
      },
      {
        expected: 'number',
        code: 'invalid_type',
        path: [ 'xp' ],
        message: 'Invalid input: expected number, received string'
      }
    ] */
  }
}

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