TL;DR
Zod v4 remains the default for TypeScript validation — but Valibot (8KB vs Zod's 60KB) and ArkType (fastest runtime parsing) are compelling for performance-critical use cases. TypeBox generates JSON Schema natively, making it the best choice for OpenAPI/Swagger integration. For new projects: Zod v4. For edge/bundle-size-critical: Valibot. For OpenAPI: TypeBox. For maximum runtime speed: ArkType.
Key Takeaways
- Zod v4: 60KB, 10M+ downloads/week, best ecosystem (react-hook-form, trpc, drizzle)
- Valibot: 8KB, tree-shakable, modular API, ~10x smaller than Zod
- ArkType: Fastest parser (3-10x faster than Zod), TypeScript syntax strings
- TypeBox: JSON Schema native,
Static<typeof Schema>TypeScript types - Performance: ArkType > Valibot > TypeBox > Zod (but all are "fast enough" for most apps)
- Ecosystem: Zod integrates with everything; others are catching up
Downloads
| Package | Weekly Downloads | Trend |
|---|---|---|
zod | ~10M | ↑ Growing |
@sinclair/typebox | ~6M | ↑ Growing |
valibot | ~1M | ↑ Fast growing |
arktype | ~200K | ↑ Growing |
Performance Benchmarks
Schema: User object with 10 fields, nested address, array of tags
Parsing 100,000 objects:
ArkType: 45ms ← Fastest
Valibot: 120ms
TypeBox: 180ms
Zod v4: 280ms (v4 is 2x faster than v3's ~580ms)
Bundle size (minified + gzipped):
Valibot: 8KB ← Smallest
ArkType: 12KB
TypeBox: 60KB (includes JSON Schema types)
Zod v4: 60KB
Type inference speed (tsc, 50-field schema):
ArkType: ~200ms
Zod v4: ~450ms
Valibot: ~600ms
TypeBox: ~300ms
Zod v4: The Default
// Zod v4 — new features and performance improvements:
import { z } from 'zod';
// Basic schema (same as v3):
const UserSchema = z.object({
id: z.string().cuid2(),
email: z.string().email(),
name: z.string().min(2).max(100),
age: z.number().int().min(0).max(150).optional(),
role: z.enum(['user', 'admin', 'moderator']),
tags: z.array(z.string()).max(10),
address: z.object({
street: z.string(),
city: z.string(),
country: z.string().length(2), // ISO 2-letter
}).optional(),
metadata: z.record(z.string(), z.unknown()),
createdAt: z.coerce.date(), // Auto-coerce string → Date
});
type User = z.infer<typeof UserSchema>;
// Zod v4 new: z.file() for Blob/File
const UploadSchema = z.object({
file: z.instanceof(File)
.refine(f => f.size < 5_000_000, 'Max 5MB')
.refine(f => ['image/jpeg', 'image/png', 'image/webp'].includes(f.type), 'Must be JPEG/PNG/WebP'),
caption: z.string().max(500).optional(),
});
// Zod v4 new: z.pipe() for chained transforms
const ParsedDateSchema = z
.string()
.pipe(z.coerce.date()); // string → validated Date
// Zod v4 new: z.toJSONSchema()
const jsonSchema = z.toJSONSchema(UserSchema);
// Generates standard JSON Schema — useful for OpenAPI docs
// Error formatting (v4 — cleaner):
const result = UserSchema.safeParse({ email: 'bad' });
if (!result.success) {
const errors = result.error.flatten();
// { fieldErrors: { email: ['Invalid email'] }, formErrors: [] }
}
Valibot: Bundle-Size Champion
// Valibot — modular, tree-shakable:
import {
object, string, number, array, optional, enum_,
email, minLength, maxLength, integer, minValue, maxValue,
parse, safeParse, flatten,
type InferInput, type InferOutput,
} from 'valibot';
// Only imports what you use — tree-shaking reduces bundle to ~2-5KB for simple schemas
const UserSchema = object({
id: string([minLength(1)]),
email: string([email()]),
name: string([minLength(2), maxLength(100)]),
age: optional(number([integer(), minValue(0), maxValue(150)])),
role: enum_(['user', 'admin', 'moderator']),
tags: array(string(), [maxLength(10)]),
});
type User = InferInput<typeof UserSchema>;
// Parse (throws on error):
const user = parse(UserSchema, rawData);
// Safe parse (returns result/error):
const result = safeParse(UserSchema, rawData);
if (result.success) {
console.log(result.output);
} else {
const errors = flatten(result.issues);
// { nested: { email: ['Invalid email'] } }
}
// Valibot with React Hook Form:
import { valibotResolver } from '@hookform/resolvers/valibot';
import { useForm } from 'react-hook-form';
function SignupForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: valibotResolver(UserSchema),
});
return (
<form onSubmit={handleSubmit(console.log)}>
<input {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
</form>
);
}
ArkType: Fastest Runtime + TypeScript Syntax
// ArkType — TypeScript-syntax strings for schemas:
import { type } from 'arktype';
// Syntax feels like writing TypeScript:
const User = type({
id: 'string',
email: 'string.email',
name: '2 <= string <= 100', // min/max length shorthand!
age: 'number.integer | undefined',
role: '"user" | "admin" | "moderator"',
tags: 'string[] <= 10', // array with max length
createdAt: 'Date',
});
type User = typeof User.infer;
// Parse:
const result = User(rawData);
// ArkType returns morph (with parse) or error:
if (result instanceof type.errors) {
console.log(result.summary); // Human-readable error
} else {
// result is User
}
// ArkType advanced: morphs (transform)
const ParsedDate = type('string').pipe(s => new Date(s), 'Date');
// Recursive types (Zod struggles here):
const TreeNode = type({
value: 'number',
children: 'TreeNode[]', // Self-referencing!
}).describe('TreeNode'); // Named for error messages
TypeBox: JSON Schema Native
// TypeBox — generates JSON Schema, used in Fastify/Hono:
import { Type, Static } from '@sinclair/typebox';
import { Value } from '@sinclair/typebox/value';
// TypeBox schema IS JSON Schema:
const UserSchema = Type.Object({
id: Type.String({ format: 'uuid' }),
email: Type.String({ format: 'email' }),
name: Type.String({ minLength: 2, maxLength: 100 }),
age: Type.Optional(Type.Integer({ minimum: 0, maximum: 150 })),
role: Type.Union([
Type.Literal('user'),
Type.Literal('admin'),
Type.Literal('moderator'),
]),
tags: Type.Array(Type.String(), { maxItems: 10 }),
});
// TypeScript type from schema:
type User = Static<typeof UserSchema>;
// Validate:
const result = Value.Check(UserSchema, rawData);
if (!result) {
const errors = [...Value.Errors(UserSchema, rawData)];
// [{ path: '/email', message: 'Expected string' }]
}
// TypeBox + Hono (validated routes with OpenAPI):
import { Hono } from 'hono';
import { describeRoute } from 'hono-openapi';
const app = new Hono();
app.post('/users',
describeRoute({
requestBody: { content: { 'application/json': { schema: UserSchema } } },
responses: { 201: { description: 'Created' } },
}),
async (c) => { /* handler */ }
);
// Export OpenAPI spec:
// app.doc('/openapi.json', { openapi: '3.0.0', info: { title: 'API', version: '1' } })
Decision Guide
Use Zod v4 if:
→ Default choice — best ecosystem (react-hook-form, trpc, drizzle, next-safe-action)
→ Team already knows Zod v3 (v4 is mostly backwards compatible)
→ Need broad library compatibility
→ Bundle size is not a constraint
Use Valibot if:
→ Edge runtime / bundle size critical (<5KB budget)
→ Want tree-shakable, pay-only-for-what-you-use
→ Cloudflare Workers or similar constrained environments
Use ArkType if:
→ Parsing millions of objects (backend hot path)
→ Love TypeScript-native syntax strings
→ Need recursive types easily
→ Fastest possible validation
Use TypeBox if:
→ Building OpenAPI/Swagger documentation
→ Using Fastify (TypeBox is Fastify's native schema)
→ Need JSON Schema output for other tools
→ API validation that also generates docs
Ecosystem and Framework Integrations
The most consequential difference between these four libraries in practice is not raw performance — it is ecosystem reach. Zod v4's integrations span virtually the entire TypeScript toolchain. React Hook Form supports Zod via @hookform/resolvers/zod, tRPC uses Zod for input/output schema definitions, Drizzle ORM exposes createInsertSchema and createSelectSchema that output Zod schemas, and next-safe-action validates Server Action inputs with Zod natively. When you choose Zod, you can use it without glue code across every layer of a full-stack application.
Valibot has caught up quickly on form validation — @hookform/resolvers/valibot ships alongside the Zod resolver, and tRPC added Valibot support in v11. The main gap remains in ORMs and server frameworks, where Zod is still the expected interface. ArkType has a growing set of adapters but does not yet have first-class support in tRPC or Drizzle. TypeBox occupies a different niche entirely: it is the native schema system for Fastify (which uses it for route-level JSON Schema validation) and integrates cleanly with Hono via hono-openapi. If you are building a Fastify or Hono API and want to auto-generate an OpenAPI 3.x spec, TypeBox is the only choice that does this without a conversion layer.
For teams choosing between Zod v4 and Valibot, a practical middle-ground is using Valibot for browser-facing form schemas (where bundle size matters) and Zod for server-side tRPC routes and database schema inference (where ecosystem compatibility matters more).
Migration from Zod v3 to v4
Zod v4 is largely backward-compatible with v3, but several subtle changes affect real codebases. The error-handling API changed: z.ZodError no longer carries .flatten() directly — you now call z.flattenError(error) or use error.flatten(). Recursive schemas that used z.lazy() now benefit from the new z.recursive() helper for better TypeScript inference. The .transform() method's behavior with .pipe() is more explicit, requiring z.pipe(schema, z.transform(...)) instead of chaining .transform() at the end of a chain in some patterns.
Bundle size improved significantly: Zod v4 is approximately 40% smaller than v3 when tree-shaken by a modern bundler, primarily because internal validators were refactored to share more code paths. The new z.toJSONSchema() function replaces the third-party zod-to-json-schema package that most codebases depended on — if you were using that package for OpenAPI generation, you can remove it. The z.file() type (for File/Blob validation) and improved z.coerce.date() behavior are additive additions that do not require migration work but are worth adopting in any code handling file uploads or date string coercion.
Performance in Production Workloads
The benchmark numbers shown earlier (ArkType 45ms, Valibot 120ms, TypeBox 180ms, Zod v4 280ms for 100K objects) reflect micro-benchmark conditions. In production API servers, the bottleneck is almost never schema validation time — it is I/O wait for database queries, external API calls, and serialization. Schema validation for a typical API request takes under 1ms with any of these libraries. The performance argument for ArkType and Valibot matters most in two specific contexts: edge runtimes with cold-start penalties (where Valibot's 8KB bundle reduces cold-start time meaningfully on Cloudflare Workers and Vercel Edge) and batch data pipelines that parse millions of records in a single Node.js process.
For the batch pipeline case, ArkType's 3-10x parsing speed advantage is genuinely material. A data ingestion job parsing 50 million rows benefits from switching from Zod to ArkType. TypeBox with its Value.Check() method also performs well in batch scenarios because it reuses the compiled validator function without re-instantiation. For standard CRUD API endpoints handling 100-1000 requests per second, all four libraries perform identically well — pick based on ecosystem fit, not benchmarks.
Compare Zod, Valibot, ArkType, and TypeBox on PkgPulse.
When to Use Each in 2026
Use Zod v4 if:
- You want the most ecosystem integrations: tRPC, React Hook Form, Prisma generators, Drizzle, and hundreds of third-party adapters
- Your team already knows Zod — the API is stable and well-documented
- You need
.transform(),.refine(), and schema inference (z.infer<typeof schema>) - You want the lowest learning curve for new team members
Use ArkType if:
- You want native TypeScript type inference without a separate schema DSL — your type IS the validator
- You want the fastest validation performance (ArkType uses JIT compilation)
- You are working on a performance-sensitive data pipeline that validates millions of objects
Use TypeBox if:
- You need JSON Schema output from your TypeScript types — TypeBox schemas ARE valid JSON Schema
- You are using Fastify (TypeBox is the official Fastify schema type)
- You want to share schemas between your API documentation (OpenAPI/Swagger) and your TypeScript code
Use Valibot if:
- Bundle size is a primary constraint — Valibot is modular and tree-shakeable to under 1KB
- You are building a library or SDK where you don't want to impose a heavy validation dependency
- You want a functional, composable API similar to Zod but with better tree-shaking
In 2026, Zod v4 remains the default for most applications. The alternatives are compelling for specific use cases: TypeBox for JSON Schema interop, ArkType for performance, Valibot for bundle-sensitive environments.
All four libraries are interoperable with standard TypeScript type inference — the TypeScript type derived from a Zod schema, an ArkType type, a TypeBox schema, or a Valibot schema is structurally identical. The choice is purely about API ergonomics, bundle size, and runtime performance.
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on Zod v4.x, ArkType v2.x, TypeBox v0.34.x, and Valibot v1.x. Zod v4 was a major refactor that improved parse performance by 3-5× over v3 and reduced bundle size. ArkType v2 introduced JIT-compiled validators that match or exceed the performance of hand-written validators. TypeBox is used as the official schema type for Fastify's route handler typing. Valibot is the only library in this comparison that ships modular sub-packages — valibot/pipe, valibot/string, etc. — enabling per-function tree-shaking that reduces the installed footprint to only the validators you import.
One underappreciated dimension of this comparison is TypeScript compilation speed. Large Zod schemas with complex generics and chained method calls impose a measurable TypeScript compiler overhead — projects with many deeply nested Zod schemas can see tsc times increase noticeably. ArkType's string-based type definitions parse faster in tsc because they rely on string literal types rather than deeply chained generic instantiations. TypeBox's Static<typeof Schema> pattern is similarly fast to type-check. For monorepos where incremental TypeScript compilation time is a bottleneck, this secondary performance dimension (TypeScript compiler speed, not runtime validation speed) can influence the library choice even before any request is handled in production.
See also: Yup vs Zod and Superstruct vs Zod, Zod v4 vs ArkType vs TypeBox vs Valibot.