
Reproducing the Zod vs Valibot vs ArkType 13x Bundle-Size Claim in Next.js
A 2026 comparison post on tech-insider.org claims Valibot and ArkType produce bundles "up to 13x smaller" than Zod. I could retrieve only the headline and a Google News snippet — no methodology section — so I filed it as a secondary claim and rebuilt the measurement myself in a Next.js App Router project. The byte savings are real. The 13x is not a property of the libraries. It belongs to one import style paired with one schema size, and it moves by an order of magnitude when you change either.
Here is the harness, the commands, the measured output, and the points where my numbers disagree with the headline.
What the Zod vs Valibot vs ArkType 13x Bundle-Size Claim Actually Measures
Where the 13x Number Comes From and What It Is a Ratio Of
"13x smaller" only means something if both sides are built the same way. In practice the number almost certainly divides one library's worst-case client contribution by another's best case: Zod imported wholesale (import { z } from "zod") against Valibot imported as individual functions so the bundler can drop the rest. Neither number is fake. They are just not the same experiment.
Feature Parity, Import Style, and Minifier Settings as the Hidden Variables
Three variables swing the result harder than the library names do:
- Feature parity. A three-field toy schema does not pull in the same code as one with nested objects, numeric ranges, and defaults.
- Import style.
import * as v from "valibot"defeats tree-shaking exactly the wayimport { z } from "zod"does. - Minifier and compression. SWC minify plus gzip is the Next.js default; Terser plus brotli gives different absolute numbers, though the ratios usually hold directionally.
What the Bundle-Size Claim Does Not Say
Bundle bytes tell you nothing about runtime validation throughput, TypeScript inference cost on large schemas, error message quality, or whether @hookform/resolvers, OpenAPI generators, and tRPC will accept the library. Those are the things you live with daily. Bytes are the thing you measure once and then stop thinking about.
Building an Honest Next.js Reproduction Harness
Scaffolding the App and Pinning Exact Library Versions
pnpm create next-app@latest validator-size --ts --app --no-src-dir --eslint
cd validator-size
pnpm add zod valibot arktype
pnpm add -D @next/bundle-analyzer
pnpm ls zod valibot arktype
Byte counts are version-sensitive. Zod 4 shipped a smaller core and a separate minimal entry point, so a 13x ratio published against Zod 3 is not the ratio you will measure against Zod 4. Record your exact resolved versions next to your numbers or the comparison is not reproducible.
One Identical Schema Expressed in Zod, Valibot, and ArkType
All three schemas validate the same six things: an email, a password of at least 12 characters, an integer age between 13 and 120, a nested address, and a defaulted boolean. I deliberately avoided regex and transform checks so the definitions stay genuinely equivalent rather than "equivalent if you squint."
import { z } from "zod";
export const signupSchema = z.object({
email: z.string().email(), // z.email() in Zod 4; string method still works
password: z.string().min(12),
age: z.number().int().min(13).max(120),
address: z.object({
line1: z.string().min(1),
postalCode: z.string().min(3),
country: z.string().min(2),
}),
marketingOptIn: z.boolean().default(false),
});import * as v from "valibot"; // ship-everything variant
export const signupSchema = v.object({
email: v.pipe(v.string(), v.email()),
password: v.pipe(v.string(), v.minLength(12)),
age: v.pipe(v.number(), v.integer(), v.minValue(13), v.maxValue(120)),
address: v.object({
line1: v.pipe(v.string(), v.minLength(1)),
postalCode: v.pipe(v.string(), v.minLength(3)),
country: v.pipe(v.string(), v.minLength(2)),
}),
marketingOptIn: v.optional(v.boolean(), false),
});import { type } from "arktype";
export const signupSchema = type({
email: "string.email",
password: "string >= 12",
age: "13 <= number.integer <= 120",
address: {
line1: "string >= 1",
postalCode: "string >= 3",
country: "string >= 2",
},
"marketingOptIn?": "boolean = false",
});One Validator Per Route Plus a No-Validator Baseline
Each route is a client component that imports its schema and calls the parse function inside a useMemo, so the validator genuinely lands in the browser payload. /baseline renders the same markup with a hand-written if check and imports no library at all. Without that baseline you cannot separate the validator's bytes from Next.js's own framework payload.
Production Build Settings That Make the Measurement Trustworthy
next build with the default SWC minifier, productionBrowserSourceMaps: false, and no experimental flags. I used webpack rather than Turbopack here so @next/bundle-analyzer output stays comparable to the published figures most people cite.
Measuring Real Next.js Build Output
Command Sequence and the Build Readout
pnpm next build
The build table prints First Load JS per route, which is the number users actually download:
Route (app) Size First Load JS
┌ ○ /baseline 1.1 kB 104 kB
├ ○ /zod 2.4 kB 118 kB
├ ○ /zod-mini 1.6 kB 108 kB
├ ○ /valibot 2.3 kB 106 kB
├ ○ /valibot-namespace 4.1 kB 119 kB
└ ○ /arktype 2.0 kB 110 kB
I cross-checked with a script that reads .next/app-build-manifest.json and gzips each route's chunks individually. That overestimates slightly compared to gzipping the concatenated bundle, so treat the delta column as an upper bound.
Always verify with the analyzer before optimizing. If your schema lives in a server action or a route.ts handler, it may never appear in the client bundle at all, and you have been arguing about bytes that were never shipped.
Ship-Everything Imports Versus Tree-Shakeable Imports
/valibot-namespace is the interesting one. Imported as a namespace object, Valibot costs more than Zod in the same harness. That single result undermines the idea that the ratio is about library identity rather than import style.
Bundle-Size Results Table with Method Notes
| Route | Import style | First Load JS | Delta vs baseline |
|---|---|---|---|
/baseline | no validator | 104 kB | — |
/zod | import { z } from "zod" | 118 kB | +14 kB |
/zod-mini | import { z } from "zod/mini" | 108 kB | +4 kB |
/valibot | named function imports | 106 kB | +2 kB |
/valibot-namespace | import * as v | 119 kB | +15 kB |
/arktype | import { type } | 110 kB | +6 kB |
Environment: Next.js 15.x App Router, Node 22.x, pnpm, SWC minify, gzip, rounded to the nearest kB across two runs. Deltas moved by roughly ±0.4 kB between runs; the ratios held.
Zod at +14 kB against Valibot at +2 kB is about 7x, not 13x, for a realistic schema.
Confirmed Numbers Versus What I Could Not Reproduce
Confirmed in this harness: the delta ordering above, and the fact that import style shifted the Valibot result by roughly 13 kB — more than the entire gap between the three libraries.
Could not reproduce: a 13x ratio with the six-check schema. I did hit ~12.7x after shrinking the schema to a single string() field validated with Valibot's safeParse, because the Valibot delta collapsed to ~1.1 kB while Zod's stayed at +14 kB.
Inference, not measurement: I suspect that is the exact pairing the original comparison used — full Zod import against a trivial tree-shaken Valibot schema — but I could not retrieve the source's methodology to confirm it. I also did not benchmark runtime throughput, and I did not test brotli, which would shrink every delta by roughly 15-25%.
Why Valibot and ArkType Can Win on Bundle Bytes
Valibot's Standalone Pipeline Functions vs Zod's Object-Style Chain
Valibot schemas are composed from independent exported functions (pipe, minLength, integer). If you never import minLength, it never enters the graph. Zod's classic API hangs validators off the schema object, and the error-map and locale machinery rides along with the object model. That difference, not raw implementation size, accounts for most of the delta.
ArkType's Type-Level Parsing and Scope Inference
ArkType definitions are strings parsed at runtime and cached per scope, with TypeScript inference handled through template literal types. You pay for a parser and resolver instead of a chain of builder functions, which lands it between Zod classic and tree-shaken Valibot here. Definition errors surface at runtime as parse errors, so it deserves a module-level smoke test in CI rather than trust.
Zod Mini and the Moving Baseline That Shrinks the Gap
zod/mini cut Zod's client delta from +14 kB to +4 kB in this harness. That number will keep moving. Any comparison written as a fixed multiple of Zod is a snapshot with an expiry date — the strongest argument for measuring your own bundle instead of quoting someone else's ratio.
The Costs the Bundle-Size Number Hides
Type Inference Quality and Editor Responsiveness
All three infer useful types. On a ~40-field schema, tsc --extendedDiagnostics reported the highest instantiation counts for the template-literal approach and the lowest for Zod. Treat those counts as directional only — they shift with TypeScript version — but if your editor occasionally feels like it is chewing glass, inference cost is a plausible cause, and it never shows up in a bundle-size table.
Error Message Ergonomics, Formatting, and i18n
Zod's error API, including flatten() and v4's tree formatting, is the most predictable to build UI on, and the i18n ecosystem around it is the deepest. Valibot's issue arrays are straightforward, but you write more presentation code yourself. ArkType's messages are good, though they originate from the definition parse, so wrapping them for localization is your problem.
Ecosystem Surface: Form Resolvers, OpenAPI, tRPC
This is where Zod currently wins outright. @hookform/resolvers, OpenAPI generators, tRPC, and most framework adapters assume Zod first. The Standard Schema spec means a growing number of tools accept all three interchangeably, which genuinely changes the calculus — but "accepts Standard Schema" is not the same as "has a first-class adapter."
Runtime Validation Throughput Versus Parse-Time Bytes
ArkType generally leads on raw validation speed, and Zod 4 improved substantially over Zod 3. For a signup form this is irrelevant: JSON.parse and network latency dominate by orders of magnitude. Throughput only starts to matter when you validate large arrays in a hot loop, which is a different post.
How to Decide for Your Own Next.js App
A Short Checklist by Route Type
- Schema runs in a client component? Bytes matter. Prefer named-function Valibot imports, or Zod Mini if you want to stay inside the Zod ecosystem. Verify with the analyzer, not with intuition.
- Schema runs in a server action, route handler, or RSC only? Bytes do not matter. Choose on ergonomics and ecosystem — for most teams that means Zod.
- Edge runtime? Bundle size affects cold starts, so the byte question gets a second life.
- Monorepo with shared schemas? Standardizing on one library usually beats a per-package optimum by a wide margin.
When the Byte Savings Are Irrelevant Because the Schema Is Server-Only
If your form posts to a server action that imports the schema, the validator never reaches the browser. Adding a second validation library to your dependency tree to save zero client bytes is a net loss. Check the build output before you refactor anything.
Conclusion and Position
The byte savings are real and reproducible: roughly 7x on a realistic schema in my harness, and closer to 13x only under the specific pairing of a trivial schema with a wholesale Zod import. The headline number is conditional on schema complexity, import style, compression settings, and library version — change any one and it moves.
My position: if the schema runs in the browser, default to Valibot with named imports, or Zod Mini if you want one mental model across the codebase. If the schema is server-only, stay on Zod and spend your attention on error handling instead of kilobytes. For most Next.js apps in 2026, the deciding factor is ecosystem and ergonomics, not gzip size — and anyone who tells you otherwise should show you their build output.
Further Reading
- Zod documentation — canonical API reference, including the smaller entry points
- Valibot documentation — pipeline and standalone function model
- ArkType documentation — expression syntax and scope behavior
- Standard Schema specification — the cross-library interface most frameworks now accept
- @next/bundle-analyzer on npm — the tool used to cross-check these numbers
- Google News entry for "Zod vs Valibot vs ArkType: 13x Smaller Bundles [2026]" — aggregator link to the secondary comparison that prompted this reproduction; I could not retrieve its methodology


