ブラウザで入力値を検証するなら、validation libraryのコードも利用者へ配信される。 そのため「Zodは大きく、Valibotは小さい」という比較には実用上の意味がある。
ただし、現在のZod 4にはtree shakingを前提にしたZod Miniがある。 regular Zodだけを相手にValibotを測れば差は大きく見えるが、実際の選択肢を一つ落とすことになる。
そこでZod 4、Zod Mini、Valibotに同じ入力契約を実装し、同じesbuild設定でbundleした。 差が残るなら、少なくともこの条件ではライブラリのAPI設計とtree shaking特性が生成物へ現れていると判断できる。
同じ入力契約を三つのAPIで表す
検証対象はユーザー情報を想定した5フィールドにそろえた。
| フィールド | 条件 |
|---|---|
name |
1文字以上の文字列 |
email |
メールアドレス形式の文字列 |
age |
0以上130以下の整数 |
tags |
最大10個の文字列配列 |
active |
boolean |
有効な入力を1件受理し、複数条件を壊した入力を1件拒否できることもbundle後のコードで確認した。 サイズだけ小さくても、三者の検証内容がずれていれば比較にならないからだ。
regular Zodではメソッドチェーンを使った。
const UserSchema = z.object({
name: z.string().min(1),
email: z.email(),
age: z.number().int().min(0).max(130),
tags: z.array(z.string()).max(10),
active: z.boolean(),
});
Zod Miniでは同じ制約をcheckとトップレベル関数へ移した。
const UserSchema = z.object({
name: z.string().check(z.minLength(1)),
email: z.email(),
age: z.number().check(z.int(), z.minimum(0), z.maximum(130)),
tags: z.array(z.string()).check(z.maxLength(10)),
active: z.boolean(),
});
Valibotではpipeにvalidation actionを並べた。
const UserSchema = v.object({
name: v.pipe(v.string(), v.minLength(1)),
email: v.pipe(v.string(), v.email()),
age: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(130)),
tags: v.pipe(v.array(v.string()), v.maxLength(10)),
active: v.boolean(),
});
Zod公式はZod Miniを、regular Zodと同等の機能を持ちながらfunctional APIによってtree shakingしやすくしたvariantとして説明している。 ValibotもAPIを小さな独立関数へ分割し、使わない部分をbundlerが除去できることを設計上の特徴としている。
三つとも「関数型APIだから同じサイズになる」わけではない。 実装内部で共有するコード量や依存関係が違うため、最終的な生成物はbundleして確認する必要がある。
esbuildの条件を固定する
測定にはesbuild 0.28.2を使った。 ライブラリはZod 4.4.3とValibot 1.4.2である。
設定は三者で共通にした。
bundle true
minify true
format esm
platform browser
target es2022
treeShaking true
legalComments none
gzip level 9
esbuildではbundle時にtree shakingが有効になり、ES Modulesのimportとexportを追跡して未使用コードを除去する。
今回はブラウザへ送るruntime validationを想定しているため、platform: browserで単一のESM bundleを生成した。
計測スクリプトは生成したbundleをそのまま読み込み、有効データの受理と無効データの拒否も実行する。
pnpm buildの前段でも同じ計測を行うため、記事に置いたJSONと実装がずれたままproduction buildを通す構成にはしていない。
gzip後では約19.6KB、5.2KB、1.8KBに分かれた
2026年8月20日の測定結果は次の通りだった。
| ライブラリ | minify後 | gzip後 | regular Zod比 |
|---|---|---|---|
| Zod 4 | 71,105 bytes | 19,573 bytes | 100% |
| Zod Mini | 15,522 bytes | 5,195 bytes | 26.5% |
| Valibot | 5,719 bytes | 1,808 bytes | 9.2% |
Zod Miniはregular Zodからgzip後のサイズを約73.5%減らした。 Zod自身がbundle制約向けのvariantを用意した効果は大きく、この比較からregular Zodだけを見てZod全体を評価することはできない。
それでもValibotはZod Miniより小さかった。 今回のschemaではgzip後1,808 bytesで、Zod Miniより約65.2%小さく、regular Zodの約9.2%に収まった。
倍率にするとregular ZodはValibotの約10.8倍、Zod Miniは約2.9倍である。
生の計測結果とbuild条件はreport.jsonから確認できる。
この数値はライブラリ公式サイトの掲載値を転載したものではなく、このサイトの実験コードを同じesbuildで生成して得た値である。
bundleサイズだけではregular Zodを捨てられない
19.6KBと1.8KBの差だけを見ると、ブラウザでは常にValibotを選べばよいように見える。 その結論は測っていない要素を含んでいる。
今回確認したのは、特定schemaの生成サイズと最小限のvalidation結果だけである。 型推論の使い勝手、エラーメッセージの組み立て、既存ライブラリとのintegration、チームがすでに持つZod schemaの移行費用までは測っていない。
Zod Miniについても同じで、公式ドキュメントはregular ZodのメソッドAPIの方が発見しやすく、Miniのfunctional APIにはDX上のトレードオフがあると説明している。 bundleサイズを5KB前後まで下げるために、その差を受け入れるかはプロジェクト側の条件で変わる。
一方、静的生成時だけ動くvalidationなら話は別である。 たとえばこのサイトのAstro Content CollectionsはZodをbuild時に使っているが、production記事ページへZodは配信されていない。 読者へ0 bytesなら、ブラウザbundleの差を理由にbuild-time validatorを置き換えても配信性能は改善しない。
ブラウザとedgeでは候補の順序が変わる
新しくブラウザ側のフォームvalidationを実装し、同等の機能で済むなら、今回の結果ではValibotを最初に試す理由がある。 1.8KBという生成物は、機能を追加する前の基準値として扱いやすい。
既存コードがZod中心で、API互換性や移行量を優先しながらサイズも下げたい場合はZod Miniが中間案になる。 今回だけでもregular Zodとの差は約14.4KB gzipあり、Valibotへ全面移行しなくても削減余地が見えた。
regular Zodはbundleサイズを最優先しない場所で残る。 長時間動くserver processやbuild-time toolingでは、数KBの転送量より既存schema、周辺ecosystem、メソッドAPIを維持する価値の方が大きくなる場合がある。
このサイトで将来Preact Islandへruntime validationを入れるなら、Valibotを基準に実際のclient chunkを測る。 その時点で初めて、1,808 bytesだった単体実験が実アプリのbundleでも維持されるかを確認できる。