Zod dominated TypeScript validation for years. Then Valibot arrived with a modular architecture and claimed 98% smaller bundle sizes. Now Zod v4 is out with a complete rewrite targeting the same criticisms.
I ran both in production-like benchmarks. Here's what actually changed.
The Core Trade-off
| Zod v4 | Valibot 1.x | |
|---|---|---|
| Bundle size | ~14kb min+gz | ~1.5kb min+gz (tree-shaken) |
| Parse speed | ~3x faster than Zod v3 | Fastest in class |
| API style | Method chaining | Functional/modular |
| Ecosystem | Massive (tRPC, react-hook-form, etc.) | Growing fast |
| Error messages | Excellent | Excellent |
| TypeScript inference | Best-in-class | Best-in-class |
Zod v4: What Actually Changed
import { z } from 'zod/v4';
// v4: significantly faster parse engine
const UserSchema = z.object({
id: z.string().uuid(),
email: z.email(), // new: first-class email type
age: z.int().min(0).max(120), // new: z.int() for integer-only
role: z.enum(['admin', 'user', 'viewer']),
metadata: z.record(z.string(), z.unknown()),
});
type User = z.infer<typeof UserSchema>;
// New in v4: z.toJSONSchema() — generate JSON Schema from Zod
const jsonSchema = z.toJSONSchema(UserSchema);
Key v4 improvements:
- Parse performance: 3-5x faster than v3 on object schemas
- New primitives:
z.email(),z.url(),z.int(),z.file() - First-class JSON Schema output
- Smaller core (still larger than Valibot when tree-shaken)
Valibot: The Functional Approach
import * as v from 'valibot';
// Every import is individually tree-shakeable
const UserSchema = v.object({
id: v.pipe(v.string(), v.uuid()),
email: v.pipe(v.string(), v.email()),
age: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(120)),
role: v.picklist(['admin', 'user', 'viewer']),
metadata: v.record(v.string(), v.unknown()),
});
type User = v.InferOutput<typeof UserSchema>;
// Parse
const result = v.safeParse(UserSchema, rawData);
if (result.success) {
console.log(result.output); // User
} else {
console.log(result.issues); // typed errors
}
The v.pipe() pattern is more verbose but every validator is a separate import — only what you use ships to the client.
Benchmark Results
Test: 100k parses of a 10-field object schema (Node 22, M2 MacBook Pro)
Zod v3: 890ms (baseline)
Zod v4: 210ms (4.2x faster)
Valibot: 140ms (6.4x faster than Zod v3)
Typebox: 95ms (compiled — fastest, but different DX)
Valibot is still faster than Zod v4, but the gap closed significantly. At 140ms vs 210ms for 100k parses, this is ~0.7μs per parse — irrelevant for API handlers, only matters for hot loops.
Bundle Size: The Real Story
Valibot's bundle advantage is real but only materializes when you tree-shake:
// If you import the whole thing, Valibot wins on bundle
import * as v from 'valibot'; // ~6kb min+gz total
import { z } from 'zod/v4'; // ~14kb min+gz total
// But if you only use 3 validators:
// Valibot: ~1.2kb shipped
// Zod v4: still ~14kb (module structure limits tree-shaking)
For edge functions, server actions, or client-side form validation, this matters. For a Node.js API server, it doesn't.
Ecosystem: Zod Still Wins
This is the factor that keeps most teams on Zod:
// tRPC — native Zod support
import { z } from 'zod/v4';
export const appRouter = router({
getUser: publicProcedure
.input(z.object({ id: z.string().uuid() }))
.query(async ({ input }) => { ... }),
});
// react-hook-form + zodResolver — battle-tested
import { zodResolver } from '@hookform/resolvers/zod';
const form = useForm<User>({
resolver: zodResolver(UserSchema),
});
// Drizzle ORM schema inference — Zod only
import { createSelectSchema } from 'drizzle-zod';
Valibot resolvers exist for react-hook-form and tRPC, but they're community-maintained and lag feature parity.
When to Use Each
Use Zod v4 when:
- You use tRPC, react-hook-form, or Drizzle-zod
- You need JSON Schema output
- Your team already knows Zod (migration cost is zero)
- You're on a Node.js server where bundle size doesn't matter
Use Valibot when:
- You're shipping validation logic to the browser/edge and every kb counts
- You're building a new project with no existing Zod dependency
- You want a functional/composable API style
- You're validating in hot loops where 70μs matters
Migration Path (v3 → v4)
Zod v4 is a separate package for now:
npm install zod@^4.0.0
// Update imports
- import { z } from 'zod';
+ import { z } from 'zod/v4'; // or 'zod' once v4 is default
// Breaking changes:
// z.string().email() → z.email()
// z.ZodError → z.core.$ZodError
// .parse() error format changed slightly
For most codebases: swap imports, fix 2-3 breaking changes, done in a day.
Validation Built Into Your Stack
The AI SaaS Starter Kit ($99) ships with Zod v4 pre-configured for tRPC, react-hook-form, and API route validation — end-to-end type safety from database to client without any setup.
The Workflow Automator MCP ($15/mo) validates all agent inputs with Zod schemas so your automations never crash on bad data.
My take: Zod v4 closed the performance gap enough that ecosystem integration wins. Unless you're shipping validation code to the browser at scale, stay on Zod and upgrade from v3 — it's a day of work and you get 4x performance for free.
Building with Valibot in 2026? I'd love to hear what pushed you there.
Top comments (0)