Editor’s note: This article was last updated on 5 September 2023 to provide a comparison of Zod and Yup based on ease of use, ecosystems, and integrations.
Web developers have to deal with a lot of complex data exchanges in their applications. It’s important for apps to validate any data they send or receive because any wrong data type can cause an error. Unexpected data types or values can also crash the application processing it and lead to other undesirable outcomes.
Schema validation is the process of verifying data by creating a schema. The definition of a schema can vary depending on the implementation, but in JavaScript, a schema is usually an object data type that is used to validate the data in question.
In this guide, I’ll compare two popular schema validation libraries, Zod and Yup, to see how they perform in terms of validating common data patterns such as primitives, objects, and arrays. I will also compare their performance metrics, learning curve, ecosystem and integrations.
Jump ahead:
Below is a simple example of a schema in JavaScript:
const userDataSchema = { name: "string", age: "number", }; const userData = { name: "Peter", age: "15", }; function validateSchema(schema, data) { for (let property in schema) { if (data[property] === undefined) { console.error(property + " is required."); } else if (typeof data[property] !== schema[property]) { console.error( property + " must be of type " + schema[property] + ", " + typeof data[property] + " found." ); } else { console.log(property + " is validated!"); } } } validateSchema(userDataSchema, userData);
In the code above, the userDataSchema
object acts as a schema, validating that the userData
object has properties of name
and age
. It also validates the type of these values: name
must be a string
while age
must be a number
.
Of course, the validateSchema
function is not enough for any serious application. To validate a complex data structure and ensure that it doesn’t cause unwanted behavior, you’ll need something more powerful. This is where a schema validation library comes in.
Fortunately, generous JavaScript developers around the world have shared myriad open source schema validation libraries on npm. Yup has been one of the most popular schema validation libraries for JavaScript, before Zod came onto the scene and stole some of Yup’s spotlight.
Zod is a zero-dependency TypeScript-first data validation library that provides a simple and intuitive way to define and validate data in both TypeScript and JavaScript applications. Zod is designed to be self-contained and lightweight with a simple syntax, making it easy to use in various TypeScript and JavaScript environments.
Some notable features of Zod include:
In this section, we will take a look at some of the common data validation APIs you’ll use.
Primitive values for Zod includes string, number, BigInt, Boolean, date, symbol, etc. Zod also provides APIs for specifying schema for empty values as well as schema for catching any type like any
and unknown
:
const zod = require('zod'); // or import { zod } from 'zod' const schema = zod.string() schema.parse('hello');
The example above shows a simple string schema validation. Zod provides extensive methods for validation:
const passwordSchema = zod.string().min(8).max(16); passwordSchema.parse("3same33");
You can also use other extensive methods for validating a number:
const ageSchema = zod.number().min(18); ageSchema.parse(12); // throws error: Number must be greater than or equal to 18
You wouldn’t want to send error messages like the one above to end users, as it wouldn’t provide the best experience. This is where custom error messages can be useful:
const ageSchema = zod .number() .min(18, { message: "too young to play" }) .max(40, { message: "too old to play" }); ageSchema.parse(12) // throws error: too young to play
Apart from validation methods, Zod also provides transformation methods that help format your string values:
const usernameSchema = zod.string().toLowerCase(); const emailSchema = zod.string().trim().email(); console.log(usernameSchema.parse("John_Doe")); // logs john_doe emailSchema.parse(" [email protected] ");
In the code above, the usernameSchema
converts the returned value of the parsed string to lower case strings. The emailSchema
uses the trim
transformation method to trim all whitespaces around the string before validating if it’s a valid email or not.
Ideally, creating a schema of “user” from the examples above would require an object. So let’s see how we can integrate all the examples above into an object schema.
const userSchema = zod.object({ username: zod.string().toLowerCase(), email: zod.string().trim().email(), age: zod.number().positive(), password: zod.string().min(8).max(16), }); userSchema.parse({ username: "john_doe", email: "[email protected]", age: -24, password: "not-my-favorite", });
In this case, Zod throws all validation errors in the order that they occur. So if both username
and age
are invalid, you’ll get an array containing error information for both schemas.
By default, all properties in an object are required. But if you want to have an optional property, you’d make use of the optional
method:
const userSchema = zod.object({ username: zod.string().toLowerCase(), email: zod.string().trim().email(), age: zod.number().positive(), password: zod.string().min(8).max(16), familySecret: zod.string().optional() });
Zod provides an array
method for validating the values of an array. For example, you can validate the minimum or maximum length of the array with the .min
and .max
functions:
const fruitsSchema = zod.array(zod.string()).min(3).max(5); fruitsSchema.parse(["orange", "banana", "apple"]);
You can pass in already created schemas to the array
method as well. Let’s re-use the userSchema
example from the previous section:
const usersSchema = zod.array(userSchema); const users = [ { username: "john_doe", email: "[email protected]", age: -24, password: "not-my-favorite", }, ]; usersSchema.parse(users); // TypeError: Number must be greater than 0
A tuple
is another special Zod API that creates an array with a fixed number of elements and various data types:
const athleteSchema = zod.tuple([ // takes an array of schemas zod.string(), // a string for name zod.number(), // a number for jersey zod.object({ pointsScored: zod.number(), }), // an object with property pointsScored that has number value ]); athleteSchema.parse(["James", 23, { pointsScored: 7 }])
The data parsed into the tuple must be an array that exactly matches the schema structure.
Zod can validate a function and make sure the data passed into the function and data returned from the function are correct. The function schema uses two APIs (args
and returns
) to validate the input and output of the function:
const sumOfNumsSchema = zod .function() .args(zod.number(), zod.number()) .returns(zod.number()); const sumOfNums = sumOfNumsSchema.validate((val1, val2) => { return val1 + val2; }); sumOfNums("1", 33); // TypeError: Expected number, received string
Unlike other validations we’ve seen so far, function validation in Zod doesn’t use the same .parse
to validate the function. Function validation is unique to Zod; Yup doesn’t have an equivalent API to perform this task.
Zod also has some unique APIs to define optional schema. For example, the union method can be used to compose “OR” types. For example, to create a schema where the data is a string “OR” a number:
const productId = zod.union([zod.string(), zod.number()]); productId.parse(222); productId.parse('I9290JEI'); productId.parse(false); // TypeError: Invalid input
Another one of Zod’s unique APIs is the intersection method, which is particularly useful for combining two schemas, creating logical “AND” types. For example:
const userId = zod.object({ id: zod.number(), }); const baseTeacher = zod.object({ name: zod.string(), }); const teacher = zod.intersection(baseTeacher, userId); teacher.parse({ name: "Mr Doe" }); // TypeError: id is required
Yup is one of the most popular JavaScript validation libraries for validating data schemas, particularly in forms and data processing. Some of its core features include:
Schema validation with Yup is similar to Zod, but the syntax is different. In terms of general use and extensibility, the two libraries perform the same operations in different ways.
For example, this is how you would validate a string in Yup:
const yup = require('yup') // or import * as yup from yup const schema = yup.string(); schema.validate(333).then((res) => console.log(res)); // returns 333 schema.isValid(11).then((res) => console.log(res)); // returns true
The validate
function is similar to Zod’s parse
function. Both functions actually parse the object instead of validating it. This means that both functions take the given data and try to return it back. If the parser encounters an error, it will terminate the runtime and throw an error.
Meanwhile, the isValid
function only validates the data and leaves error handling to you. So you may consider catching the errors when using the validate
function of Yup and the parse
function of Zod.
From the example above, you may probably be wondering why the schema that we specified as string
doesn’t throw any error when a number is passed as the value. This is because Yup isn’t strict by default. A number can easily be coerced into a string, and as such no error is thrown. But you can override this default behavior with the strict
API:
const schema = yup.string().strict(); schema.validate(333).then((res) => console.log(res)); // TypeError: this must be a `string` type, but the final value was: `333`
Just like Zod, Yup also provides extensive APIs for validation and transformation:
const userSchema = yup .object({ username: yup.string().lowercase().required(), email: yup.string().email().required(), age: yup.number().positive().required(), password: yup.string().min(8).max(16).required(), familySecret: yup.string(), }) .strict(); userSchema .validate({ username: "John_Doe", email: "[email protected]", password: "not-my-favorite", }) .then((res) => console.log(res)); // TypeError: age is a required field
For array type, Yup has several useful extensions to validate its values. For example, you can validate the minimum or maximum length of the array with the .min
and .max
functions. You can also check the type of its value with the .of
function:
// validate that the data is an array with number as its value. // The minimum value of the array is two // The minimum length of the array is four const schema = yup.array().of(yup.number().min(2)).min(4); schema.validate([2]).then((res) => { console.log(res); // typerror: this field must have at least 4 items });
Both Zod and Yup support TypeScript. Zod offers TypeScript first-class support. These libraries enable you to infer TypeScript type aliases that you can use to validate the data.
In simple terms, you can validate whether a variable is the correct type of data by creating a type
alias from Yup or Zod’s schema:
import * as yup from "yup"; import * as zod from "zod"; const yupSchema = yup.string() type A = yup.InferType<typeof yupSchema> const x: A = 12 // wrong, but nothing happens const zodSchema = zod.string(); type B = zod.infer<typeof zodSchema>; // string const y: B = 12; // TypeError
You can run the script above using TypeScript. Notice that Zod actually throws an error while Yup does nothing, even though the value of x
should be a string instead of a number.
Comparing the performance of both libraries depends heavily on the specific use case, the complexity of your validation schemas, and the size of the data being validated.
For simple validation rules and small datasets, the performance difference between Zod and Yup may not be tangible. However, for very complex schemas or large datasets, the performance characteristics may differ. So consider benchmarking the specific validation scenarios that are critical to your application.
Both Yup and Zod are very simple to learn and easy to use. Their syntaxes are so similar that you can easily switch your entire codebase from one to the other.
Zod is designed with TypeScript as a primary consideration, which means it integrates seamlessly with TypeScript projects, unlike Yup, which was built to support TypeScript.
So if you already have a TypeScript project, consider using Zod. Zod is also known for its zero dependency, making it lightweight and easy to integrate into your projects without worrying about extra dependencies.
Both Zod and Yup have comprehensive documentation, which makes learning easier and faster. Both libraries have a large community adoption, ensuring robust community support so you won’t be stuck on a bug for too long.
Though Yup is very popular for its easy integration with popular form libraries like Formik, Zod also integrates well with other form libraries like React Hook Form. Sometimes it may require additional third-party libraries to make integrating Zod with these form libraries seamless.
Zod and Yup are not the only JavaScript and TypeScript libraries for schema validation. In fact, they are not as popular as libraries like joi and AJV.
joi is a popular JavaScript validation library used primarily for defining and enforcing data validation rules. It’s widely used in both server-side and client-side applications. It has its own schema definition syntax similar to what we’ve seen in Yup and Zod.
It’s safe to say that AJV is the most popular schema validation library. Unlike joi, which focuses on schema-based validation, AJV is specifically designed for JSON schema validation. It adheres to the JSON Schema standard and provides efficient validation of JSON data against JSON schemas. AJV is often used in APIs and data processing pipelines.
joi and AJV are more focused on complex validation scenarios and are suitable for handling large and intricate validation rules. Yup and Zod are often preferred for simpler use cases and form validation.
As you can see from the comparisons above, Zod and Yup both have simple APIs to validate data using schema. Yup has some functions outside of validating data, such as the number schema’s truncate and round methods, which may come in handy in a specific situation.
Zod is capable of validating a function’s input and output to make sure it has all the right data. It also has great TypeScript support, which terminates the runtime in case of errors, while Yup simply does nothing when the inferred type is wrong. What’s more, Zod has some unique features to define optional schemas like union and intersection.
So which schema validation library should you use for your next project? It depends on your application requirements. I recommend using Yup if you do a lot of form validation because its extensive functions cover many patterns that are used in forms, even situational ones where you have to do a rounding.
But if you have lots of API data exchange and you need to validate all data that passes between client and server, Zod might be your best bet — especially if you’re using TypeScript.
Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.
LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowSOLID principles help us keep code flexible. In this article, we’ll examine all of those principles and their implementation using JavaScript.
JavaScript’s Date API has many limitations. Explore alternative libraries like Moment.js, date-fns, and the new Temporal API.
Explore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
One Reply to "Comparing schema validation libraries: Zod vs. Yup"
Amazing article,Thanks for sharing this informative blog with us.