Zod vs Yup vs Valibot in 2026: Schema Validation Showdown
Zod, Yup, and Valibot each take a different bet on TypeScript schema validation. Here's which one actually wins in 2026 and why.
Why Schema Validation Still Matters in 2026
TypeScript's type system is fantastic right now. But it only protects you at compile time. The second real data arrives — from a form, an API, a query param — you're flying blind unless you validate it at runtime too. That gap is exactly what Zod, Yup, and Valibot all fill.
In practice, picking the wrong library here costs you. Bundle size, inference quality, error message ergonomics, and how well the schema doubles as your source of truth for TypeScript types — these things compound fast when your codebase has 50 schemas instead of five. Honestly, most teams grab Zod without thinking, and that's usually fine, but it's not always the right call.
Worth noting: all three libraries integrate with React Hook Form, the de-facto standard for form state in React apps. So the integration story isn't really a differentiator anymore — you need to look at the fundamentals.
This comparison uses the latest releases available as of mid-2026: Zod 3.23, Yup 1.4, and Valibot 0.33. Numbers will shift, but the architectural differences between them are stable.
Zod: The Safe Default
Zod has been the community's default since roughly 2022, and it earned that spot. The API is clean, the TypeScript inference is excellent, and .parse() vs .safeParse() is an obvious enough distinction that even junior devs get it on day one. You write a schema, you get a type — done.
import { z } from 'zod';
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
age: z.number().min(18).max(120),
role: z.enum(['admin', 'user', 'guest']),
});
type User = z.infer<typeof UserSchema>;
// Safe parse — never throws
const result = UserSchema.safeParse(rawData);
if (!result.success) {
console.error(result.error.flatten());
} else {
const user = result.data; // fully typed
}The transformation story is also solid. .transform(), .preprocess(), .pipe() — you can massage data on the way in without writing a bunch of manual code. That matters when you're talking to APIs that send numbers as strings, which is basically all of them.
That said, Zod's bundle size is the elephant in the room. It ships around 12kb gzipped as of 3.23. That's not catastrophic for a Node or full-stack app, but on the browser side — especially if you're validating forms in a component that loads on every page — it adds up. Quick aside: if you're already paying the Zod tax in your server bundle and you're on Next.js, sharing schemas between server and client is actually a win.
Where Zod struggles is error customization. The default error map works, but customizing per-field messages requires either a global error map or chaining .message() calls on every rule. It's fine. Not great, not terrible.
Yup: The Veteran With Baggage
Yup has been around since the Formik era — we're talking 2017. It pioneered the 'schema as source of truth' idea for JavaScript, and a huge amount of legacy code depends on it. If you're maintaining a codebase that's five or more years old, you've almost certainly seen Yup.
import * as yup from 'yup';
import { InferType } from 'yup';
const userSchema = yup.object({
id: yup.string().uuid().required(),
email: yup.string().email().required(),
age: yup.number().min(18).max(120).required(),
role: yup.mixed<'admin' | 'user' | 'guest'>().oneOf(['admin', 'user', 'guest']).required(),
});
type User = InferType<typeof userSchema>;
try {
const user = await userSchema.validate(rawData, { abortEarly: false });
} catch (err) {
if (err instanceof yup.ValidationError) {
console.error(err.errors); // array of error messages
}
}The TypeScript inference in Yup has improved a lot since 1.0, but it's still weaker than Zod. Things like discriminated unions and branded types are either awkward or impossible. You'll hit edge cases where the inferred type is any or wider than you'd expect, and then you're back to manual casting.
Honestly, Yup's main value prop in 2026 is that it's already in your project. If you're not migrating, don't migrate just for the sake of it. But if you're starting fresh? There's no compelling reason to pick Yup over the alternatives — the API is more verbose, tree-shaking is weaker, and the async-first design (.validate() returns a promise) adds overhead you probably don't need for synchronous validation.
One more thing — Yup's .when() conditional logic is genuinely expressive and easier to read than Zod's .superRefine() for complex cross-field validation. If your schemas are heavy on conditionals, that's worth factoring in.
Valibot: The Bundle-Size Disruptor
Valibot is the newcomer that actually changes the calculus. Released in 2023 and now at 0.33, it's built from the ground up with tree-shaking as a first principle. Instead of method chaining on a single object, every validator is a standalone function that your bundler can drop if you don't import it.
import { object, string, number, email, uuid, minValue, maxValue, pipe, parse, safeParse } from 'valibot';
// Only what you import gets bundled
const UserSchema = object({
id: pipe(string(), uuid()),
email: pipe(string(), email()),
age: pipe(number(), minValue(18), maxValue(120)),
});
type User = InferOutput<typeof UserSchema>;
const result = safeParse(UserSchema, rawData);
if (!result.success) {
console.error(result.issues);
}The payoff is real. A minimal Valibot validation payload can be under 1kb gzipped, compared to Zod's ~12kb. For a UI library like Empire UI where components are meant to be lean and composable, that philosophy resonates. If you're building a design system or component library where consumers might use only one or two of your forms, Valibot lets them pay only for what they ship.
That said, the API is unfamiliar at first. If you've been writing Zod for two years, Valibot feels backwards — functions instead of methods, pipe() for chaining, InferOutput vs z.infer. It clicks after a day, but the learning curve is real and your whole team needs to make that jump together. TypeScript inference is on par with Zod in most cases, though complex schemas can still produce surprising results.
The ecosystem is catching up fast. React Hook Form, tRPC, and most major form libraries added Valibot adapters by early 2025. The docs are good. If you're starting a greenfield project today and bundle size is a concern, Valibot is a legitimate first choice.
Head-to-Head: The Numbers That Actually Matter
Let's put the key metrics side by side so you can make a quick call without reading three changelogs. These numbers are based on real bundleanalyzer output and the official benchmarks from each library's repo as of 2026.
Library | Bundle (min+gz) | TS Inference | Async | Tree-shakable
-----------|-----------------|--------------|-------|---------------
Zod 3.23 | ~12 kb | Excellent | Yes | Partial
Yup 1.4 | ~11 kb | Good | First | Partial
Valibot 0.33 | 0.3–2 kb* | Excellent | Yes | Full
* Depends entirely on which validators you importFor parse performance, all three are fast enough that you won't notice in a normal web app. Valibot edges out Zod in microbenchmarks by 20-30%, but unless you're validating thousands of objects per second on the client, it doesn't matter.
Error formatting is where Zod wins on DX. error.flatten() and error.format() give you structured errors that map cleanly to form fields without extra wiring. Valibot's issues array is more verbose out of the box. Yup's array of strings is the least structured, which means more glue code when you want per-field errors in a component like an address form.
One real-world scenario worth calling out: if you're using tRPC to validate API inputs, you'll want Zod or Valibot — tRPC's first-class support is cleanest with those two. Yup integration exists but feels like an afterthought. For form-heavy UIs inspired by styles from our glassmorphism components or any other aesthetic-first design system, Valibot's payload advantage matters most in the browser layer.
When to Pick Each One
Here's the honest breakdown. Pick Zod when you want the path of least resistance, need excellent documentation, and your app is server-heavy enough that 12kb of validation overhead doesn't move the needle. It's also the clear winner for teams already invested in the Zod ecosystem — zod-to-json-schema, drizzle-zod, trpc + zod — where the integrations are first-class.
Pick Valibot when you're building something that runs in the browser, you care about bundle size at a component or library level, and you're willing to spend half a day getting your team comfortable with the functional API. Greenfield projects in 2026 should seriously consider Valibot as the default, especially if you're building a component library or micro-frontend where consumers tree-shake aggressively.
Pick Yup only if it's already in your project and the cost of switching outweighs the benefits, or you need heavy conditional validation with .when() and don't want to rewrite those chains. There's no shame in staying with Yup — it's stable, well-understood, and 1.4 is genuinely better than the 0.x days. Starting fresh with it in 2026, though, is a choice that'll feel dated faster than the alternatives.
Look, there's no universally correct answer here. The right library is the one your team will actually read the errors from and maintain over time. That said, Valibot's tree-shaking model is where the ecosystem is heading — the same way pnpm's content-addressable store became the default for monorepos, functional tree-shakable APIs are becoming the default for validation. You can pick that up now or catch up later.
FAQ
Yes. Zod 3.23 has the best ecosystem integration, excellent TypeScript inference, and a DX that's hard to beat. If bundle size isn't a constraint, it's still a solid default.
Significantly. Zod ships around 12kb gzipped minimum. Valibot can come in under 1kb if you only import a handful of validators — it's fully tree-shakable by design.
All three have official resolvers: @hookform/resolvers/zod, @hookform/resolvers/yup, and @hookform/resolvers/valibot. Setup is nearly identical across all three.
Yes, as of tRPC v11, Valibot is supported alongside Zod. The integration is first-class and the API feels natural if you're already using Valibot for form validation.