zod vs valibot 徹底比較

zod の詳細valibot の詳細
AI生成コンテンツ

この記事はAIによって生成されました。内容の正確性は保証されません。最新の情報は公式ドキュメントをご確認ください。

Zod vs Valibot — TypeScriptバリデーションライブラリ徹底比較

1. 結論

バンドルサイズを極限まで削りたいなら Valibot、エコシステムの充実度と安定性を重視するなら Zod を選んでください。 Valibot はツリーシェイキングに最適化された関数ベース設計で、使った分だけバンドルに含まれます。一方 Zod は圧倒的なユーザー数とサードパーティ連携の豊富さで、プロダクション導入のハードルが低いライブラリです。


2. 比較表

観点Zod (v3.x)Valibot (v1.x)
GitHub Stars≈ 35,000+≈ 7,000+
npm 週間DL数≈ 2,500万+≈ 200万+
ミニファイ+gzip サイズ約 14 kB(全体)約 1〜5 kB(使用分のみ)
ツリーシェイキング△(メソッドチェーンのため限定的)◎(関数ベースで完全対応)
API スタイルメソッドチェーン(OOP 風)パイプ+関数合成(FP 風)
TypeScript 対応◎(TypeScript-first)◎(TypeScript-first)
型推論 (Infer)
エラーメッセージのカスタマイズ◎(errorMap / メッセージ引数)◎(各バリデーションに直接指定)
非同期バリデーション
エコシステム連携◎(React Hook Form, tRPC, Conform, Drizzle 等多数)○(対応拡大中、主要ライブラリは対応済み)
学習コスト低〜中低〜中
初回リリース2020年2023年
ランタイム依存なしなし

3. それぞれの強み

Zod の強み

  • 圧倒的なエコシステム: tRPC・React Hook Form・Conform・Next.js Server Actions・Drizzle ORM など、多くのライブラリが Zod をファーストクラスでサポートしています。「とりあえず Zod を入れておけば連携で困らない」という安心感があります。
  • メソッドチェーンの直感性: z.string().email().min(1) のようにドットで繋ぐ API は、IDE の補完との相性が良く、スキーマの可読性も高いです。
  • 成熟度と情報量: Stack Overflow・Zenn・Qiita などの日本語記事も豊富で、トラブルシューティングが容易です。
  • .transform() / .refine() の柔軟性: バリデーションとデータ変換をスキーマ内で完結でき、複雑なビジネスロジックにも対応しやすい設計です。

Valibot の強み

  • バンドルサイズの圧倒的な小ささ: 関数単位でインポートするため、使わないバリデーションはバンドルに含まれません。Zod 比で最大 90% 以上 のサイズ削減が可能です。
  • 関数ベースの合成可能な設計: pipe() で関数を合成する FP スタイルにより、カスタムバリデーションの作成・再利用が自然に行えます。
  • モジュラーアーキテクチャ: 各機能が独立した関数として提供されるため、バンドラーの最適化が最大限に効きます。
  • Zod 互換のマイグレーションパス: API の概念(parsesafeParseInfer など)が Zod と似ているため、Zod 経験者は短時間で移行できます。

4. コード例で比較

4-1. 基本的なユーザースキーマの定義と型推論

Zod

import { z } from "zod";

const UserSchema = z.object({
  name: z.string().min(1, "名前は必須です"),
  email: z.string().email("有効なメールアドレスを入力してください"),
  age: z.number().int().min(0).max(150).optional(),
  role: z.enum(["admin", "editor", "viewer"]),
});

// 型を自動推論
type User = z.infer<typeof UserSchema>;
// => { name: string; email: string; age?: number; role: "admin" | "editor" | "viewer" }

// バリデーション実行
const result = UserSchema.safeParse({
  name: "田中太郎",
  email: "tanaka@example.com",
  role: "admin",
});

if (result.success) {
  console.log(result.data); // 型安全な User オブジェクト
} else {
  console.error(result.error.issues);
}

Valibot

import * as v from "valibot";

const UserSchema = v.object({
  name: v.pipe(v.string(), v.minLength(1, "名前は必須です")),
  email: v.pipe(v.string(), v.email("有効なメールアドレスを入力してください")),
  age: v.optional(v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(150))),
  role: v.picklist(["admin", "editor", "viewer"]),
});

// 型を自動推論
type User = v.InferOutput<typeof UserSchema>;
// => { name: string; email: string; age?: number; role: "admin" | "editor" | "viewer" }

// バリデーション実行
const result = v.safeParse(UserSchema, {
  name: "田中太郎",
  email: "tanaka@example.com",
  role: "admin",
});

if (result.success) {
  console.log(result.output); // 型安全な User オブジェクト
} else {
  console.error(result.issues);
}

4-2. カスタムバリデーション(パスワード確認の一致チェック)

Zod

import { z } from "zod";

const PasswordFormSchema = z
  .object({
    password: z.string().min(8, "8文字以上で入力してください"),
    confirmPassword: z.string(),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: "パスワードが一致しません",
    path: ["confirmPassword"],
  });

type PasswordForm = z.infer<typeof PasswordFormSchema>;

const result = PasswordFormSchema.safeParse({
  password: "securePass123",
  confirmPassword: "securePass999",
});

if (!result.success) {
  // [{ message: "パスワードが一致しません", path: ["confirmPassword"], ... }]
  console.error(result.error.issues);
}

Valibot

import * as v from "valibot";

const PasswordFormSchema = v.pipe(
  v.object({
    password: v.pipe(v.string(), v.minLength(8, "8文字以上で入力してください")),
    confirmPassword: v.string(),
  }),
  v.forward(
    v.check(
      (data) => data.password === data.confirmPassword,
      "パスワードが一致しません"
    ),
    ["confirmPassword"]
  )
);

type PasswordForm = v.InferOutput<typeof PasswordFormSchema>;

const result = v.safeParse(PasswordFormSchema, {
  password: "securePass123",
  confirmPassword: "securePass999",
});

if (!result.success) {
  // [{ message: "パスワードが一致しません", path: [...], ... }]
  console.error(result.issues);
}

4-3. データ変換(transform)

Zod

import { z } from "zod";

const DateStringSchema = z
  .string()
  .datetime()
  .transform((val) => new Date(val));

type Result = z.infer<typeof DateStringSchema>;
// => Date

const date = DateStringSchema.parse("2025-01-15T09:00:00.000Z");
console.log(date instanceof Date); // true

Valibot

import * as v from "valibot";

const DateStringSchema = v.pipe(
  v.string(),
  v.isoTimestamp(),
  v.transform((val) => new Date(val))
);

type Result = v.InferOutput<typeof DateStringSchema>;
// => Date

const date = v.parse(DateStringSchema, "2025-01-15T09:00:00.000Z");
console.log(date instanceof Date); // true

5. どちらを選ぶべきか — ユースケース別の推奨

Zod を選ぶべきケース

ユースケース理由
tRPC / React Hook Form との連携が前提ファーストクラスサポートがあり、設定がほぼ不要
チーム開発で統一ライブラリを決めたい情報量・採用実績が圧倒的に多く、オンボーディングが楽
サーバーサイド(API バリデーション)中心バンドルサイズの制約が緩く、エコシステムの恩恵を最大限に受けられる
既存プロジェクトで Zod が使われている無理に移行するメリットは薄い

Valibot を選ぶべきケース

ユースケース理由
クライアントサイドのバンドルサイズを極限まで削りたいツリーシェイキングにより使用分だけバンドルされる
Edge Runtime / Cloudflare Workersサイズ制限が厳しい環境で真価を発揮
関数型プログラミングスタイルを好むチームpipe() ベースの合成が自然にフィットする
新規プロジェクトで軽量スタートしたい依存の少なさと小さなフットプリントが魅力
Zod からの段階的移行を検討中概念が似ているため学習コストが低い

どちらでも良いケース

  • 小〜中規模の個人プロジェクト: どちらを選んでも開発体験に大きな差はありません。好みの API スタイルで選んで問題ありません。

6. まとめ

Zod と Valibot は、どちらも TypeScript ファーストのバリデーションライブラリとして非常に高い完成度を持っています。解決する課題は同じですが、設計思想が異なります

  • Zod は「メソッドチェーンによる直感的な API」と「巨大なエコシステム」が武器です。迷ったらまず Zod を選べば、情報量とサードパーティ連携で困ることはほぼありません。
  • Valibot は「関数ベースのモジュラー設計」と「圧倒的なバンドルサイズの小ささ」が武器です。クライアントサイドのパフォーマンスやエッジ環境での制約がある場合に、強力な選択肢となります。

最終的には、プロジェクトの制約(バンドルサイズ・実行環境・チームの慣れ・連携ライブラリ) に応じて選択するのがベストです。どちらも活発にメンテナンスされており、長期的に安心して採用できるライブラリです。