🐡

ZodではなくValibotを使う(フロントエンドでは)

2025/03/06に公開

知ってる人も多いとは思いますが、フロントエンドでは Zod ではなく Valibot を使ったほうがバンドルサイズが減るよという話です。

ZodもValibotも、検証したデータに対してTypeScriptの型をつけてくれる便利なライブラリですが、大きな違いの一つとして、Valibotはtree-shaking可能ですが、Zodはtree-shaking可能な設計になっていないということです。tree-shakingとは、ライブラリ内にある利用していないコードをバンドルから除外して、アプリケーションのバンドルサイズを減らすことを指します。

フロントエンド開発で利用するバンドラは基本的に tree-shaking に対応しているので、フロントエンドでこういったライブラリを使う場合には、ZodよりもValibotを使ったほうがバンドルサイズが小さくなるということです。

で、実際どれぐらい違うかと言うと、これは実際に rollup というViteの裏側で使われているバンドラを使って検証してみました。

ここにValibotとZodを使った似たコードがあります。これらを実際にバンドルしてみます

valibot_object.js:

import { string, parse, object, number, boolean } from 'valibot';

const schema = object({
  name: string(),
  age: number(),
  isActive: boolean(),
});

console.log(
  parse(schema, { name: "hoge", age: 10, isActive: true })
);

zod_object.js:

import { string, object, number, boolean } from 'zod';

const schema = object({
  name: string(),
  age: number(),
  isActive: boolean(),
});

console.log(
  schema.parse({ name: "hoge", age: 10, isActive: true })
);

rollupにはJavaScriptから扱うAPIがあるので、次のコードのように叩いてバンドルを生成してあげます。

import { rollup } from "rollup";
import nodeResolve from "@rollup/plugin-node-resolve";
import terser from "@rollup/plugin-terser";

const bundle = await rollup({
  input: inputFile,
  plugins: [nodeResolve(), terser()],
  treeshake: true, // 未使用のコードを削除
});

await bundle.write({
  file: outputPath,
  format: "cjs"
});

すると、以下のような結果になりました。バンドルサイズはTerserプラグインでminifyした結果の大きさとgzip圧縮した場合の大きさです。

case minified gzipped
valibot_object.js 3035B 1171B
zod_object.js 54487B 12414B

Valibotを使ったケースでは、バンドルサイズはZodを使っているケースと比べて10倍近く小さくなっています。Valibotを使ったバンドルではtree-shakingによってバンドルサイズが小さくなっていますが、Zodを使ったバンドルではあまり小さくなっていないことがわかると思います。

計測するために使ったスクリプトは、zod-vs-valibotというリポジトリにあげていますので詳細が気になる方は見てみてください。そんじゃーね!

Discussion