← Back to blog
··14 min read

Zod vs Yup vs Valibot in 2026: Which TypeScript Validation Library to Build On

ZodValibotYupValidationTypeScriptNext.jsFormsDX
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

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:

  • Define a schema — the expected fields, types, and constraints of a value.
  • Parse / validate untrusted input against that schema at runtime.
  • Return either a trusted, typed value or a structured error.
  • Infer (for the TypeScript-first ones) a static type from the schema, so the type and the check share one source of truth.
  • Compose small schemas into bigger ones — objects, arrays, unions, nested shapes.
  • 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

    ZodValibotYup
    DesignTypeScript-firstTypeScript-first, modularJavaScript-first
    Type inferenceExcellent (`z.infer`)Excellent (`InferOutput`)Weak / leaky
    Bundle sizeModerate (better in Zod 4)Very small (tree-shakable)Moderate
    API styleChained methodsFunctions + `pipe`Chained methods
    EcosystemLargestGrowing fastMature but older
    Standard SchemaYesYesNo
    Async validationYesYesYes (a strength)
    Best fitMost new projectsBundle-sensitive codeExisting 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.

    ts
    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 error

    What 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.

    ts
    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.

    ts
    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 leakier

    What 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 itz.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

    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:

  • Client-side validation — a form on a public marketing page, a client component — every kilobyte is downloaded by every visitor.
  • Edge and size-constrained serverless — where bundle limits and cold-start time are real constraints.
  • Where it matters far less:

  • Server-only validation — an API route, a background job, env parsing at boot. The schema never reaches the client, so a few kilobytes are irrelevant and you should pick on DX instead.
  • 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:

  • Switching later is cheap. Moving from Zod to Valibot (or back) no longer means rewriting every integration, because the surrounding tools don't care which one you hand them.
  • You can mix per-context. Valibot for a bundle-sensitive client form, Zod for server-side API validation — in the *same* project — and both are accepted.
  • 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

    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:

  • Use one validation library consistently across forms, API boundaries, and config.
  • Reuse the same schema on client and server wherever you can.
  • Confirm a fresh install validates real input on the first try.
  • 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:

  • You're starting a new TypeScript project and want the safest, best-documented default
  • You want the largest ecosystem — tRPC, react-hook-form, drizzle-zod, OpenAPI
  • You value DX and expressiveness — discriminated unions, transforms, branded types
  • You're building a template or starter to sell and want code buyers instantly recognize
  • Choose Valibot if:

  • Your validation ships to the browser or runs on the edge
  • Bundle size is a real constraint and you're counting kilobytes
  • You want Zod-style type inference with a modular, tree-shakable footprint
  • You're building something whose selling point is speed or size
  • Choose Yup if:

  • You're maintaining an existing Yup/Formik codebase
  • You rely on its mature async and conditional validation
  • Your team is already standardized on it and there's no reason to churn
  • 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.

  • "New TypeScript project, want it safe and well-documented" → Zod
  • "Client-side or edge, counting kilobytes" → Valibot
  • "Maintaining an existing Formik app" → Yup
  • "Selling a template that must read as modern" → Zod (or Valibot if size is the pitch)
  • "Want to switch later without pain" → Zod or Valibot (both do Standard Schema)
  • 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.

    Frequently asked questions

    What does a validation library actually do, and why do I need one?

    A schema validation library checks that data matches a shape you define — the right fields, the right types, within the right constraints — at runtime, when the data actually arrives. TypeScript only checks types at compile time and then disappears; it cannot verify a JSON body from a fetch request, a form submission, an environment variable, or a webhook payload, because all of those are 'any' the moment they cross into your program at runtime. That gap is exactly where bugs and security issues live: a missing field, a string where you expected a number, an email that isn't an email. Zod, Yup, and Valibot let you declare a schema once — for example 'email is a valid email string, age is a positive integer' — and then parse untrusted input against it. If it passes, you get back a fully typed, trusted value; if it fails, you get a structured error you can show the user or log. The best of them (Zod, Valibot) also infer the static TypeScript type from that same schema, so you define the shape once and get both runtime safety and compile-time types instead of writing the interface twice and letting them drift apart. Anywhere untrusted data enters your app — forms, API routes, env config, third-party payloads — is where you need one.

    Is Zod or Valibot smaller, and does bundle size actually matter?

    Valibot is the smaller one, by design, and whether that matters depends on where the code runs. Zod ships as a more monolithic module: importing z pulls in a large surface even if you only use a few validators, though Zod 4 improved tree-shaking and reduced its footprint versus Zod 3. Valibot was built the opposite way — every validator is a separate function you import individually and compose with a pipe, so a bundler can drop everything you don't use. In practice a small Valibot schema can be several times lighter than the equivalent Zod schema in the final client bundle. Where that matters: code that ships to the browser (form validation on a landing page, a client component) and code that runs at the edge or in size-constrained serverless functions, where cold-start time and bundle limits are real. Where it matters far less: server-only validation (an API route, a background job, env parsing at boot) where the schema never reaches the client and a few extra kilobytes are irrelevant. So the honest answer is: if your validation is server-side, pick on DX and ecosystem, and Zod usually wins; if your validation ships to the client or runs on the edge and you're counting kilobytes, Valibot's modular design is a genuine, measurable advantage.

    Why is Zod recommended over Yup for TypeScript projects?

    Because Zod was designed TypeScript-first and Yup was not, and that difference shows up in the part you use every day: type inference. In Zod you write one schema and call z.infer to get a static TypeScript type that is guaranteed to match the runtime validation — the type and the check can never drift, because they come from the same source. Yup started as a JavaScript object-schema library and had TypeScript support added later; its inference exists but is weaker and leakier, so you more often end up hand-writing an interface alongside the schema or fighting mismatches between what Yup validates and what TypeScript believes. Zod's API is also more expressive for TypeScript patterns developers actually need — discriminated unions, branded types, transforms that change the output type, and precise inference through .optional(), .nullable(), and refinements. On top of that, the ecosystem has moved: tRPC, drizzle-zod, react-hook-form's most common examples, and countless OpenAPI and form tools assume Zod. Yup is mature and perfectly functional — if you're maintaining a Formik-based app already built on it, there's no urgency to migrate — but for a new TypeScript project the combination of true single-source type inference and ecosystem gravity makes Zod the default.

    What is Standard Schema and why does it matter for choosing?

    Standard Schema is a small shared interface that Zod, Valibot, and ArkType all adopted so that other libraries can accept 'any validation library' instead of hardcoding one. Before it, a tool like a form library or an API framework had to write a specific adapter for each validation library — a Zod resolver, a Yup resolver, a Valibot resolver — and you were somewhat locked into whichever ones your tools supported. With Standard Schema, a library can say 'give me any Standard-Schema-compatible schema' and it works with Zod, Valibot, or ArkType out of the box. This matters for your decision in two ways. First, it lowers the risk of picking 'wrong': because the major libraries expose the same interface, switching from, say, Zod to Valibot later is far less painful than it used to be — the surrounding tools don't care which one you hand them. Second, it means you can often choose per-context — Valibot for a bundle-sensitive client form, Zod for server-side API validation in the same project — and still have your form library, tRPC, or router accept both. It turns the choice from a hard lock-in into a reversible, mix-and-match decision, which is why 'just start with Zod' is low-risk advice: you can move without rewriting your whole app.

    Which validation library is best for forms in React and Next.js?

    For most React and Next.js forms in 2026, the standard setup is react-hook-form plus a schema validation library through a resolver, and Zod is the most common and best-documented choice — nearly every react-hook-form tutorial and starter uses the Zod resolver, so you'll find answers to any problem you hit. The pattern is clean: define one schema for the form, pass it to the resolver, and react-hook-form validates on submit (or on change) and surfaces field-level errors, while z.infer gives you a typed form-values object for free. Valibot is an excellent choice for the same job when the form ships in a client bundle and you care about size — react-hook-form supports it via a resolver too (and increasingly via Standard Schema), so you get the same developer experience with a smaller footprint, which is attractive for public marketing pages and client-heavy apps. Yup is the historically dominant option because it paired with Formik for years, so if you're in an existing Formik codebase, staying on Yup is fine and well-trodden. The extra advantage of Zod or Valibot here is that the same schema you use for the client form can be reused to validate the request on the server — one schema, both sides of the boundary — which removes an entire class of 'the client allowed it but the server rejected it' bugs. For a new project: react-hook-form + Zod is the safe default, swap in Valibot when bundle size is a priority.

    Which validation library should a template or starter kit ship with?

    For most templates and starters you sell, Zod is the strongest default, for the same reasons it's a strong default generally: it's what buyers expect to see, it's the most documented, and its single-source type inference means the code reads as modern and type-safe at a glance. When a buyer opens a Next.js SaaS starter and finds Zod schemas validating the API routes, the forms, and the environment variables — with types inferred from those same schemas — it signals the codebase was built with current best practices, and they already know how to extend it. The strong ecosystem also means the template's other pieces (tRPC, react-hook-form, an ORM integration like drizzle-zod, OpenAPI docs) compose cleanly, so you're not asking the buyer to learn a bespoke setup. Reach for Valibot instead when the template's selling point is performance or size — an edge-first app, a widget or embeddable, a client-heavy experience where you're advertising a small bundle — because there the smaller footprint is part of the value proposition and worth the slightly smaller ecosystem. Yup makes sense mainly if the template is intentionally Formik-based for an audience that expects that stack. Whichever you pick, the resale signal is coherence: use one validation library consistently across forms, API boundaries, and config; reuse the same schema on client and server where you can; and make sure a fresh install validates real input on the 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.

    Related guides

    Browse Quality-Scored Code

    Every listing on CodeCudos is analyzed for code quality, security, and documentation. Find production-ready components, templates, and apps — or sell your own code and keep 90%.

    Browse Marketplace →