· zod / valibot / schema-validation

Zod vs Valibot: Đánh đổi khi chọn schema validation năm 2026

Valibot giảm 12× bundle so với Zod — nhưng Zod thắng về hệ sinh thái, i18n và Astro Actions. Số liệu thực tế, code thực tế, một khuyến nghị rõ ràng.

Bởi

2.312 từ · 12 phút đọc

Chọn Zod nếu bạn đang chạy Node.js server, đã có Zod trong stack, hoặc dùng Astro Actions. Chọn Valibot nếu bạn deploy lên browser hoặc edge và 15 KB là thứ bạn cần tính — bundle của Valibot sau tree-shaking cho một form đăng nhập điển hình chỉ 1.37 kB, so với 17.7 kB của Zod, chênh lệch 12 lần thể hiện rõ trong cold-start metrics. Cả hai đều validate TypeScript schema chính xác trong năm 2026. Quyết định nằm ở bundle size, độ phù hợp với hệ sinh thái, và quán tính của team — không phải độ chính xác.

Bài này dành cho ai

Các TypeScript developer đang chọn schema validator cho dự án mới, hoặc đang cân nhắc chuyển từ cái này sang cái kia. Nếu bạn đang dùng Zod và hài lòng, phần hệ sinh thái và kết luận sẽ chỉ ra các tình huống cụ thể mà phép tính có thể thay đổi.

Phiên bản dùng trong bài

Tất cả số liệu bên dưới thuộc về các bản phát hành cụ thể này:

Thư việnPhiên bảnNgày phát hành
Zodv4.4.34 tháng 5, 2026
Valibotv1.4.05 tháng 5, 2026

Tổng quan nhanh

Thông sốZod v4.4.3Valibot v1.4.0
Lượt tải hàng tháng~595M~32.9M
GitHub stars42,7328,690
Bundle đầy đủ (gzip)60.3 KB14.4 KB
Bundle form điển hình (esbuild)~17.7 kB~1.37 kB
Kiểu APIOOP có chainingFunctional composition
Tree-shakingMột phần (xuất sắc trong Zod Mini)Xuất sắc theo thiết kế
Yêu cầu TypeScriptv5.5+ strictv5+
Không phụ thuộc ngoài
Giấy phépMITMIT
Phát hành gần nhấtTháng 5/2026Tháng 5/2026

Cả hai đều được duy trì tích cực, không có dependency ngoài, MIT. Zod có lượt tải gấp 17 lần. Chênh lệch bundle size là trục duy nhất mà một bên thắng rõ ràng.

Bundle size

Đây là con số duy nhất thực sự ảnh hưởng đến deployment của bạn.

Bundlephobia (đo ngày 21/5/2026) cho kết quả toàn bộ package:

Zod 4.4.3Valibot 1.4.0
Minified274.7 KB83.4 KB
Gzip60.3 KB14.4 KB

Nhưng import cả thư viện không phải cách bạn dùng những công cụ này. Hướng dẫn so sánh của Valibot xây dựng một form validator đăng nhập thực tế với esbuild và đo những gì thực sự gửi đến client:

Thư việnKết quả esbuild
Valibot v11.37 kB
Zod Mini6.88 kB
Zod (standard)17.7 kB

Tại sao lại có khoảng cách lớn như vậy? Các function của Valibot là độc lập. Import v.string()v.email() chỉ bundle hai function đó và không gì khác. API class-chaining của Zod có nghĩa là import namespace z kéo theo toàn bộ prototype chain.

Zod v4 đã giới thiệu Zod Mini (import { z } from "zod/mini") — một functional API tương tự style của Valibot giúp cắt bundle xuống còn ~3.94 kB với Rolldown. Điều đó thu hẹp khoảng cách, nhưng Valibot vẫn thắng ở 1.37 kB.

Trên một Node.js API server, chênh lệch 16 kB gần như vô hình — chỉ là sai số làm tròn của cold-start. Trên Cloudflare Worker với giới hạn CPU time chặt chẽ, hay trong Next.js client component nơi mỗi kilobyte ảnh hưởng đến Time to Interactive, thì nó có ý nghĩa. Hãy biết deployment target của bạn trước khi coi con số này là quyết định.

Ví dụ API

Parse và safeParse

Cả hai thư viện đều có parse có thể throw exception và một biến thể safe trả về discriminated union.

Zod v4:

import { z } from "zod"

const UserSchema = z.object({
  name: z.string(),
  age: z.number().int()
})

// Throw ZodError nếu thất bại
const user = UserSchema.parse({ name: "Alice", age: 30 })

// Không bao giờ throw — success: true | success: false
const result = UserSchema.safeParse(input)
if (result.success) {
  console.log(result.data)
} else {
  console.error(result.error.issues)
}

// Biến thể async cho async refinements
const user2 = await UserSchema.parseAsync(input)

Valibot v1:

import * as v from "valibot"

const UserSchema = v.object({
  name: v.string(),
  age: v.pipe(v.number(), v.integer())
})

// Throw ValiError nếu thất bại
const user = v.parse(UserSchema, { name: "Alice", age: 30 })

// Không bao giờ throw
const result = v.safeParse(UserSchema, input)
if (result.success) {
  console.log(result.output)  // .output, không phải .data
} else {
  console.error(result.issues)
}

// Biến thể async
const user2 = await v.parseAsync(UserSchema, input)

Cấu trúc gần như giống hệt. Điểm khác biệt trên bề mặt: Valibot dùng standalone function (v.parse(schema, input)) trong khi Zod dùng method trên schema object (schema.parse(input)). Kết quả success của Valibot dùng .output; Zod dùng .data.

Refine, transform, coerce

Zod:

// Custom validation
z.string().refine(s => s.startsWith("https"), "Must be HTTPS URL")

// Transform — kiểu output thay đổi so với kiểu input
z.string().transform(s => s.split(","))  // z.infer<> = string[]

// Coerce — bọc primitive constructors
z.coerce.number()  // Number(input)

Valibot:

// check() là tương đương của refine()
v.pipe(v.string(), v.check(s => s.startsWith("https"), "Must be HTTPS URL"))

// transform bên trong pipe
v.pipe(v.string(), v.transform(s => s.split(",")))  // InferOutput<> = string[]

// Không có namespace coerce — dùng explicit transform action
v.pipe(v.unknown(), v.transform(v => Number(v)))

v.pipe() của Valibot làm mỗi bước validation thành một argument tường minh. Cú pháp method chaining của Zod dễ đọc hơn với schema đơn giản; pipe của Valibot trở nên dễ đọc hơn khi schema có nhiều bước validation và transformation, vì mỗi bước được tách biệt rõ ràng.

Brand types

Cả hai đều xử lý nominal typing tại compile time với zero runtime cost.

Zod:

const UserId = z.string().brand<"UserId">()
type UserId = z.infer<typeof UserId>  // string & { _brand: "UserId" }

Valibot:

const UserId = v.pipe(v.string(), v.brand("UserId"))
type UserId = v.InferOutput<typeof UserId>  // string & Brand<"UserId">

Không có sự khác biệt thực tế. Cả hai tạo ra guard chỉ tồn tại trong TypeScript, biến mất hoàn toàn ở runtime.

Hiệu suất runtime

Zod v4 là bản viết lại từ đầu. Benchmark chính thức cho thấy:

Loại schemaTốc độ tăng v4 so với v3
Parse string14.71×
Parse array7.43×
Parse object6.5×

Valibot v1 có throughput runtime tương đương Zod v4. Valibot từng nhanh hơn Zod v3 khoảng 2 lần — lợi thế đó không còn nữa. Hướng dẫn so sánh của Valibot xác nhận cả hai hiện ngang nhau.

Với hầu hết ứng dụng, cả hai đều parse hàng nghìn object mỗi giây mà không trở thành bottleneck. Nếu throughput thô là ưu tiên hàng đầu và bạn cần tốc độ AOT-compiled, thì cả hai đều không phải câu trả lời — TypeBox hay Typia mới là công cụ phù hợp. Còn lại, hiệu suất runtime không nên là yếu tố quyết định.

Zod v4 cũng cắt giảm đáng kể thời gian compile TypeScript — giảm 100 lần số lượng type instantiation so với v3. Với các monorepo lớn nơi tsc chạy chậm trên những file schema lớn, đây là cải tiến thực sự.

Hỗ trợ từ hệ sinh thái

Standard Schema đã thay đổi bức tranh đáng kể. Bất kỳ framework nào đã áp dụng spec này đều hỗ trợ Zod, Valibot, ArkType, TypeBox và các thư viện khác có thể hoán đổi cho nhau:

Tích hợpZodValibotGhi chú
tRPCCả hai qua Standard Schema
React Hook Form@hookform/resolvers hỗ trợ cả hai
Drizzle ORMCả hai tích hợp sẵn trong drizzle-orm core
TanStack FormCả hai được hỗ trợ
HonoCả hai được liệt kê
Vercel AI SDKCả hai được hỗ trợ
SvelteKit SuperformsCả hai có adapter
Astro ActionsAstro đi kèm Zod; không có đường chính thức cho Valibot

Astro Actions là ngoại lệ đáng chú ý duy nhất. Astro ship astro/zod như một dependency đi kèm — tính đến tháng 5/2026 vẫn chưa có Valibot adapter chính thức. Nếu Astro Actions có trong stack của bạn, Zod là con đường ít ma sát nhất.

Với tRPC, React Hook Form, và Drizzle, việc chọn bên nào là trung lập. Standard Schema nghĩa là cả hai hoạt động như nhau trong những framework này — không có penalty cho adapter, không thiếu tính năng. Nếu bạn còn đang chọn ORM, Drizzle vs Kysely bao quát các đánh đổi TypeScript ORM. Nếu bạn đang cân nhắc cách tiếp cận RPC, tRPC vs GraphQL có phân tích đầy đủ.

Thông báo lỗi và i18n

Zod v4 đi kèm ba utility format lỗi ngay từ đầu:

const result = z.object({ age: z.number() }).safeParse({ age: "old" })

z.flattenError(result.error)
// { formErrors: [], fieldErrors: { age: ["Expected number, received string"] } }

z.treeifyError(result.error)   // cấu trúc lồng nhau khớp với shape của schema
z.prettifyError(result.error)  // chuỗi dễ đọc cho logging

i18n tích hợp qua zod/locales hỗ trợ hơn 40 ngôn ngữ:

import { de } from "zod/locales"
z.config(de())  // thông báo lỗi bằng tiếng Đức trên toàn app

Valibot v1 có utility flatten(issues) cho output thân thiện với form và package cộng đồng @valibot/i18n cho dịch thuật — ít locale hơn bộ built-in của Zod, nhưng danh sách đang tăng thêm.

Nếu bạn cần i18n mà không muốn thêm dependency, 40 locale tích hợp của Zod là lợi thế thực sự. Nếu app của bạn chỉ cần tiếng Anh, cả hai ngang nhau.

Khi nào nên chọn Zod vs Valibot

Chọn Zod khi:

  • Server-side hoặc Node.js — bundle size không quan trọng; giữ lựa chọn chiếm ưu thế trong hệ sinh thái
  • Astro Actions — Zod đi kèm sẵn, zero friction
  • Đã có Zod trong stack — migration tốn thời gian thực; lý luận về bundle size không áp dụng cho server code
  • Cần i18n tích hợp — hơn 40 locale mà không cần cài thêm
  • TypeScript codebase lớn — số lượng type instantiation giảm của Zod v4 cải thiện tốc độ tsc
  • Kiến thức team và cộng đồng — 595M lượt tải hàng tháng nghĩa là nhiều tutorial, câu trả lời Stack Overflow hơn, và starter template mặc định dùng Zod

Chọn Valibot khi:

  • Edge function hoặc browser code — Cloudflare Workers, Vercel Edge, client-side bundle nơi 12–16 kB có ý nghĩa
  • Dự án mới năm 2026 — Valibot v1 ổn định và tài liệu tốt; khoảng cách hệ sinh thái gần như đã đóng lại với người dùng Standard Schema
  • Ưa thích functional compositionv.pipe() tường minh hơn method chaining cho schema phức tạp nhiều bước
  • tRPC / React Hook Form / Drizzle là các tích hợp của bạn — Standard Schema làm cho việc chọn bên nào trung lập; hãy quyết định dựa trên bundle size

Đường migrate

Valibot cung cấp codemod chính thức và migration guide xử lý phần lớn công việc máy móc:

  • Automated codemod (CLI + công cụ online)
  • Thay thế import (zodvalibot)
  • Tái cấu trúc method chain → v.pipe()
  • Bảng đổi tên (andintersect, catchfallback, enumpicklist)

Một schema layer có độ phức tạp vừa phải với ranh giới rõ ràng mất một buổi chiều, không phải cả sprint.

Kết luận

Server-side / full-stack Node.js: Zod. Quán tính hệ sinh thái, chiều sâu tài liệu, và lượng adoption gấp 17 lần lấn át những tiết kiệm bundle mà bạn sẽ không bao giờ thấy trong production metrics. Astro Actions đã khóa bạn ở đó rồi.

Edge, browser, hoặc dự án mới: Valibot. Giảm 12 lần bundle size cho một form validator điển hình là tiết kiệm thực sự trong môi trường cold-start. Valibot v1 ổn định, Standard Schema nghĩa là các tích hợp framework của bạn như nhau, và đường migrate về Zod đã được tự động hóa nếu bạn đổi ý.

Nếu bạn đã có Zod, hãy ở lại với Zod — lý luận cho việc chuyển đổi chỉ có giá trị với client-side hoặc edge deployment, và migration tốn công thực. Nếu bạn đang chọn lần đầu hôm nay và deploy ở bất kỳ nơi nào cần tính đến bytes, Valibot đáng để bỏ qua chi phí làm quen ban đầu.


Lỗi validation trong production biểu hiện thành runtime error. Nếu bạn chưa capture chúng với đầy đủ schema-path context, Sentry làm điều này mà không cần thêm instrumentation — bạn thấy field nào thất bại, input là gì, và full stack.

Muốn đi sâu hơn về TypeScript type inference và cách Zod, Valibot schema kết hợp với các pattern nâng cao hơn? Frontend Masters có những khóa TypeScript có cấu trúc bao phủ lĩnh vực này.

Tham khảo