Zod vs Yup vs Valibot in 2026: Which TypeScript Validation Library to Build On
The Boundary Where TypeScript Stops Protecting You
TypeScript feels like a safety net until you remember when it disappears. Every type you write is erased at compile time, so the moment real data crosses into your program at runtime — a JSON body from a fetch, a form submission, an environment variable, a Stripe webhook — TypeScript has already left the building. That data is any, and any is where the bugs and the security holes live: the missing field, the string that should have been a number, the "email" that isn't one.
A schema validation library closes that gap. You declare the shape of the data once, then *parse* untrusted input against it at runtime. If it passes, you get back a trusted, fully typed value; if it fails, you get a structured error to show the user or log. The best libraries infer the static TypeScript type from that same schema — so you define the shape once and get both the runtime check and the compile-time type, instead of writing the interface twice and watching them drift apart.
In 2026 three names dominate that job: Zod, the TypeScript-first default with the biggest ecosystem; Valibot, the modular, tree-shakable challenger built for tiny bundles; and Yup, the mature, JavaScript-first veteran that powered a generation of Formik forms. This guide compares them through two lenses: which is better to build on, and which produces a project that's clean to hand off or sell.
Developer writing TypeScript on screen
First, What These Libraries Actually Share
All three do the same core job, so it helps to name it before comparing. A schema validation library lets you:
They all validate the same kinds of untrusted data: forms, API request bodies, environment config, third-party payloads. The differences are in *how* they express schemas, *how well* they infer types, and *how much they weigh* in your bundle — and that's where DX, size, and ecosystem diverge.
At a Glance
| Zod | Valibot | Yup | |
|---|---|---|---|
| Design | TypeScript-first | TypeScript-first, modular | JavaScript-first |
| Type inference | Excellent (`z.infer`) | Excellent (`InferOutput`) | Weak / leaky |
| Bundle size | Moderate (better in Zod 4) | Very small (tree-shakable) | Moderate |
| API style | Chained methods | Functions + `pipe` | Chained methods |
| Ecosystem | Largest | Growing fast | Mature but older |
| Standard Schema | Yes | Yes | No |
| Async validation | Yes | Yes | Yes (a strength) |
| Best fit | Most new projects | Bundle-sensitive code | Existing Formik apps |
Note: all three evolve quickly. Treat this table as a map, not a spec sheet, and verify current behaviour against the official docs before you commit.
The Design Decision That Explains Each One
Almost every difference below follows from one bet each library made about how a schema should be expressed and shipped.
Zod: TypeScript-first, batteries included
Zod's bet is that the schema and the type should come from the same place, and that developer experience beats everything. You write a schema with a fluent, chained API, and one call gives you the static type.
import { z } from "zod";
const User = z.object({
email: z.string().email(),
age: z.number().int().positive(),
role: z.enum(["admin", "member"]),
});
type User = z.infer<typeof User>; // { email: string; age: number; role: "admin" | "member" }
const result = User.safeParse(input); // typed value or structured errorWhat you get is the best DX and the biggest ecosystem — expressive support for discriminated unions, branded types, transforms, and precise inference, plus first-class integrations everywhere. What you pay is a larger footprint than Valibot, though Zod 4 narrowed that gap with better tree-shaking and speed. It's the natural default for a project you want to be readable and easy to extend — which is also why it reads well when you hand off or sell the code.
Valibot: modular, tree-shakable, tiny
Valibot's bet is that most of a validation library's weight is code you never use. So instead of one big object with methods, every validator is a separate function you import individually and compose with a pipe. A bundler can then drop everything you don't touch.
import * as v from "valibot";
const User = v.object({
email: v.pipe(v.string(), v.email()),
age: v.pipe(v.number(), v.integer(), v.minValue(1)),
role: v.picklist(["admin", "member"]),
});
type User = v.InferOutput<typeof User>;
const result = v.safeParse(User, input);What you get is a dramatically smaller bundle — often several times lighter than the equivalent Zod schema — with the same single-source type inference. What you pay is a younger, smaller ecosystem and a slightly more verbose pipe style. It's the right answer when your validation ships to the browser or runs on the edge, where kilobytes and cold starts are real — the same "remove per-run friction" instinct behind picking a lean tech stack for web apps in 2026.
Yup: mature, JavaScript-first
Yup's bet predates the TypeScript-first era. It's an object-schema library with a chained API, born in the Formik years, and it's genuinely mature — async validation, conditional schemas, and a large body of existing code all lean on it.
import * as yup from "yup";
const User = yup.object({
email: yup.string().email().required(),
age: yup.number().integer().positive().required(),
role: yup.string().oneOf(["admin", "member"]).required(),
});
type User = yup.InferType<typeof User>; // works, but inference is leakierWhat you get is a battle-tested library with strong async and conditional validation. What you pay is that it was JavaScript-first, so its type inference is weaker — you more often hand-write an interface alongside the schema or fight mismatches. It's the right answer when you're maintaining an existing Yup/Formik codebase, rarely the pick for a greenfield TypeScript app.
Type Inference: The Difference You Feel Every Day
This is where TypeScript-first design pays off, and it's the single biggest reason Zod and Valibot beat Yup for new work.
With Zod or Valibot, you define the schema and derive the type from it — z.infer or InferOutput. The runtime check and the static type are guaranteed to agree because they share one source. Change the schema, the type updates; there's no second definition to keep in sync.
With Yup, the schema is the source of truth for validation but a weaker one for types. Inference exists, but it leaks around optionals, transforms, and conditionals, so teams frequently write an interface next to the schema — reintroducing exactly the drift the library was supposed to prevent. It's the same reason a TypeScript-first stack beats bolting types on afterward: one source of truth, not two.
Type-safe data flow across an application boundary
Bundle Size: Where Valibot Wins, and Where It Doesn't Matter
Valibot's headline advantage is size, and it's worth being precise about *where* you feel it.
Because every Valibot validator is a separately imported function, your bundle contains only the validators you actually use. Zod historically shipped a larger, more monolithic surface — importing z pulled in a lot even for a small schema — though Zod 4 improved tree-shaking and shrank the footprint. The gap is real: a small Valibot schema can be several times lighter than the Zod equivalent in the final client bundle.
Where that matters:
Where it matters far less:
So the honest rule: server-side, choose on ecosystem and DX (usually Zod); client-side or edge with a kilobyte budget, Valibot's modular design is a measurable win.
Standard Schema: Why the Choice Is No Longer a Lock-In
The most important 2026 development is that Zod, Valibot, and ArkType all adopted Standard Schema — a small shared interface that lets other libraries accept "any validation library" instead of hardcoding one.
Before it, every tool needed a specific adapter — a Zod resolver, a Yup resolver, a Valibot resolver — and you were somewhat locked into whatever your tools supported. Now a form library, router, or API framework can say "give me any Standard-Schema-compatible schema" and it works with Zod or Valibot out of the box.
This changes the decision in two ways:
That's why "just start with Zod" is genuinely low-risk advice in 2026: the exit is easy. (Yup does not implement Standard Schema, another reason it's fading for new work.)
Forms, APIs, and Config: The Three Places You'll Reach For This
Validation shows up at every boundary. The winning move with Zod or Valibot is to reuse one schema on both sides.
Forms (React / Next.js)
The standard setup is react-hook-form + a resolver. Zod is the most documented pairing — nearly every tutorial and starter uses it — and Valibot works the same way via its resolver (and increasingly via Standard Schema). One schema validates the form *and*, reused on the server, validates the request — killing the entire class of "the client allowed it but the server rejected it" bugs.
API boundaries
Validate every request body at the edge of your handler, whether you're on REST, GraphQL, or tRPC. tRPC in particular is built around Zod-style schemas, and pairing your validator with your ORM via something like drizzle-zod lets one schema describe the database row, the API contract, and the inferred type at once.
Environment variables
Parse process.env through a schema at boot so a missing or malformed variable fails *loudly on startup* instead of silently at 2 a.m. This one habit prevents a shocking share of production incidents — and it's the kind of detail that separates a production-ready codebase from a demo.
Form validation and secure data entry
Which Reads Better When You Sell the Code
If you build templates or starters to sell — the whole point of CodeCudos — the validation library is a signal buyers read for how modern and type-safe the code is, exactly like the auth layer, the ORM, or the package manager.
Zod is the stronger default for something you'll sell. It's what buyers expect to open — the most documented, the most recognizable, and its single-source inference means the code reads as current at a glance. When a buyer finds Zod validating the API routes, the forms, and the env config in a Next.js SaaS starter, with types inferred from those same schemas, it reads as care — and they already know how to extend it.
Ship with Valibot when the template's selling point *is* performance or size — an edge-first app, an embeddable widget, a client-heavy experience where a small bundle is part of the value proposition.
Ship with Yup mainly when the template is intentionally Formik-based for an audience that expects that stack.
Whichever you pick, the real resale signal is coherence and a clean first run:
A starter that hand-writes types next to unused schemas, or mixes two validation libraries, undercuts the "production-ready" impression no matter which one it's built on — the same coherence-over-hype standard that keeps any codebase credible.
How to Choose
Choose Zod if:
Choose Valibot if:
Choose Yup if:
If you're still unsure:
For a new TypeScript project you own end to end — the common case — start with Zod. It's the best-documented, has the deepest ecosystem, and thanks to Standard Schema the exit to Valibot is cheap if bundle size later becomes the priority. Reach for Valibot when kilobytes matter from day one, and stay on Yup only when you're already invested.
The Bottom Line
There's no universal winner — there's a right validation library for how much you value ecosystem and DX versus bundle size, and who inherits the code.
Whichever you choose, the habit that outlasts the decision is the same: validate untrusted data at every boundary — forms, API routes, and environment config — and reuse one schema on both sides so the type and the check never drift. That discipline costs almost nothing and prevents an entire category of runtime bugs the moment code leaves your hands.
Ready to turn what you build into income? List your template or app on CodeCudos, see how validation fits the wider stack in our best tech stack for web apps in 2026 guide, compare the ORM layer with Drizzle vs Prisma, the auth layer with Clerk vs Auth.js vs Better Auth, or make sure the whole codebase reads as production-ready.
