SpikeMe
← Comparisons
Data validation

Zod vs Yup

updated on July 13, 2026

Zod vs Yup

Zod and Yup compared on type inference, required-by-default fields, and form integration — with real npm data and a verdict by context.

FactZodYup
Current version4.4.31.7.1
Downloads/week (npm)224,111,97810,111,879
LicenseMITMIT
Bundle (gzip)60.3 kB13.1 kB
CriterionZodYup
Required fieldsrequired by default, .optional() to releaseoptional by default, .required() to enforce
Source of the TypeScript typez.infer<typeof schema>yup.InferType<typeof schema>
String format validationstop-level functions (z.email(), z.url())chained methods (string().email(), string().url())
Historical forms integrationadopted by more recent libraries (react-hook-form resolvers, tRPC)historically tied to the Formik ecosystem
Schema compositionz.object({...}).extend / z.unionobject({...}).shape / mixed().oneOf
Minimal bundlelarger, single library with everything includedsmaller, more isolated functions per type

Context

A field that receives no explicit requiredness rule behaves in two opposite ways depending on which validation library you choose — and that single design decision is the root of practically every practical difference between Zod and Yup. In Zod, every field of a z.object({...}) is required by default, and only becomes optional when explicitly marked with .optional(). In Yup, it's the opposite: every field of an object({...}) is optional by default, and only becomes required with .required(). That inversion propagates straight into the TypeScript type inferred for each schema, and is the most common source of bug surface when someone switches from one library to the other without reviewing the fields one by one.

Yup is also older in the JavaScript ecosystem and was born before the mass adoption of TypeScript, which shows in how type-inference support was added — as a layer on top of an already existing API, rather than being the design's starting point, as it is in Zod.

When to choose Zod

Zod fits when the team wants the schema to be the single source of truth and the TypeScript type to derive from it with no room for silent divergence — every declared field is required, unless stated otherwise explicitly:

import { z } from "zod";

const User = z.object({
  email: z.email(),
  name: z.string(),
});

type User = z.infer<typeof User>; // { email: string; name: string } — ambos obrigatórios

No field of the User type above is optional, because none was marked with .optional() — the default behavior is the strictest one. That starting point reduces the risk of a field accidentally becoming optional by oversight, which is especially relevant in API payload validation, where a field "forgotten as optional" can let incomplete data through without an error. Zod has also become the standard adopted by more recent libraries in the TypeScript ecosystem — form resolvers, schema generation for tRPC, and AI tools that need structured schemas — which widens its integration surface beyond traditional forms.

When to choose Yup

Yup remains a solid choice in projects that already have a mature Yup schema base, or that use Formik — the Formik + Yup combination is one of the oldest and most documented in the React ecosystem for form validation:

import { object, string, InferType } from "yup";

const userSchema = object({
  email: string().email(),
  name: string().required(),
});

type User = InferType<typeof userSchema>;
// { email?: string | undefined; name: string } — email é opcional por padrão

Note that, in the example above, email has no .required() and so the inferred type marks it as optional — even though it's present in the schema. That behavior might be exactly what a form wants (fields that only become required under certain conditions, something Yup expresses well with .when()), but it demands extra attention from anyone expecting the "required by default" behavior that Zod has accustomed part of the ecosystem to. Yup also has mature, long-standing support for conditional validation between fields and for asynchronous tests, a use case present since the library's earliest versions.

Verdict

Teams that already bet on Formik read this comparison one way; greenfield teams read it another — the answer changes depending on the starting point, not on which library is "better." If the team wants TypeScript to be treated as the source of truth, with fields required by default and less risk of silent divergence between schema and type, Zod tends to be the safer choice for new projects. If the project already runs on Formik or has an established Yup schema base, or if the "optional by default, required when needed" semantics fits the domain better (forms with conditional fields), Yup remains a mature, well-documented option.

That inversion of defaults is also the most common mistake in poorly reviewed migrations between the two: a forgotten .required(), or a .optional() that should have been there, goes unnoticed until an incomplete payload reaches production. Reviewing field by field remains the only safe way to swap one for the other — no automated script replaces that manual work.

Zod

Choose Zod if…

Choose Zod if the team wants TypeScript to be the source of truth — schema and derived type always in sync, with fields required by default.

Yup

Choose Yup if…

Choose Yup if the project already uses Formik or a legacy codebase built around yup, or if you prefer optional-by-default fields with explicit required calls.

The numbers above came from the registry, with source and date. Still, this comparison is generic: the right answer depends on versions, your team and what already exists in your project.

Generate the spike for YOUR stack

Share

XLinkedIn