Valibot
website·Indexed 37 days ago
https://valibot.dev/A modular and type-safe schema validation library. The documentation covers installation, core mental models for schemas and pipelines, validation for complex data structures (objects, records, arrays), and async validation. It includes API specifications for core types, utility functions, and migration guidance for v0.31.0 via codemods.
What's inside Valibot
- Valibot is a modular, type-safe schema library used to validate data from servers, forms, or configuration files. It features static type inference, a small bundle size (starting under 700 bytes), and has no dependencies, making it compatible with any JavaScript environment. Unlike TypeScript types, Valibot schemas are executed at runtime to guarantee type safety of unknown data.
Understand Valibot's internal schema and action architecture
Valibot is designed as a modular system of plain object literals returned by pure factory functions, enabling aggressive tree-shaking.
- Schemas: Objects satisfying
BaseSchemathat validate a specific data type. They include properties likekind: 'schema',type(snake_case), and a~runfunction for parsing datasets. - Validation Actions: Objects satisfying
BaseValidationthat check a typed value and may add issues (e.g.,min_length). - Transformation Actions: Objects satisfying
BaseTransformationthat convert a value to a new type or value (e.g.,trim).
- Schemas: Objects satisfying
Understand Valibot issue object properties
When validation fails, Valibot returns issue objects containing detailed information about the error. Key properties include:
kind: The category of the problem.'schema'indicates a data type mismatch (e.g., number instead of string);'validation'indicates the type is correct but the content is invalid (e.g., regex mismatch).type: The specific function that triggered the validation error (e.g.,'array','min_length').input: The specific piece of input data where the issue occurred.expected: A language-neutral string describing the expected data property.received: A language-neutral string describing the received data property.message: A human-readable error message (customizable via i18n or quick start settings).requirement(Optional): Additional validation constraints, such as the specific minimum length required byminLength.path(Optional): An array ofPathItemobjects describing the location of the issue within complex data structures.issues(Optional): Used specifically withunionschemas to contain all issues from the union's member schemas.config: Contains configuration state likelang,abortEarly, andabortPipeEarly.
Assign metadata titles to schemas with v.title
Thev.titleaction attaches a semantic title to a schema. This is primarily used for documentation purposes or when integrating with AI tools to provide better context about the field's purpose.const UsernameSchema = v.pipe( v.string(), v.regex(/^[a-z0-9_-]{4,16}$/iu), v.title('Username'), v.description('A username must be between 4 and 16 characters long...') );Merge object schemas in v0.31.0
Thev.merge()function has been removed because of the introduction of multiple object schema types (loose, strict, etc.). To merge objects, manually spread theentriesproperty of the schemas into a newv.object()call.const ObjectSchema1 = v.object({ foo: v.string() }); const ObjectSchema2 = v.object({ bar: v.number() }); // New way to merge const MergedObject = v.object({ ...ObjectSchema1.entries, ...ObjectSchema2.entries, });Understand the Valibot mental model
Valibot's modular API is built around three core concepts:
- Schemas: The starting point for validation. They define a specific data type (e.g., string, object) and can be nested to create complex structures.
- Methods: Functions used to modify or utilize a schema. The schema is always passed as the first argument to the method (e.g.,
v.parse(schema, data)). - Actions: Functions used to further validate, transform, or annotate data. Actions are used exclusively within a
v.pipe()to extend a schema's functionality.
Handle validation issues using BaseIssue
When a schema or action fails, it adds aBaseIssueobject to the dataset. This object contains details about the failure, including the input value, expected vs received values, and the path to the error in nested structures.BaseIssuealso inherits fromConfigto carry i18n and abort settings.// BaseIssue properties: { kind: 'schema' | 'validation' | 'transformation', type: string, input: unknown, expected: string | null, received: string, message: string, requirement: unknown | undefined, path: IssuePathItem[] | undefined, issues: BaseIssue[] | undefined }Migrate from Joi to Valibot: Basic Setup and Structure
Migrating from Joi involves updating imports and shifting from method chaining to a pipeline-based architecture. In Valibot, validation and transformation actions are passed tov.pipe()following the initial schema. Methods like.validate()are replaced by standalone functions likev.safeParse()orv.parse(), where the schema is passed as the first argument.import * as v from 'valibot'; // Chaining vs Pipeline // Joi: Joi.string().email().max(30) const Schema = v.pipe(v.string(), v.email(), v.maxLength(30)); // Validation // Joi: const { value, error } = Joi.string().validate('foo'); const result = v.safeParse(v.string(), 'foo'); if (result.success) { console.log(result.output); } else { console.log(result.issues); } // Throwing exceptions (like Joi.attempt) const value = v.parse(v.string(), 'foo');Compare Valibot bundle size and performance with Zod
Valibot is designed for minimal bundle size through a modular, tree-shakable architecture. For a standard login form validation, Valibot requires approximately 1.37 kB, which is a ~90% reduction compared to Zod (17.7 kB with esbuild) and a ~73% reduction compared to Zod Mini (6.88 kB with esbuild).
Regarding performance:
- Startup Performance: Valibot excels in Time to Interactive (TTI) benchmarks due to its small bundle size and efficient initialization.
- Runtime Performance: Valibot is roughly twice as fast as Zod v3 and similar to Zod v4/Zod Mini. It is slower than compiler-based libraries like Typia or TypeBox because it does not use a compiler to generate optimized runtime code or the
Functionconstructor.
Validate that an object does not have a specific number of entries using v.notEntries
Thev.notEntriesaction validates that the number of entries in an object (such as av.recordorv.object) does not equal the specifiedrequirement.const NotEntriesSchema = v.pipe( v.record(v.string(), v.number()), v.notEntries(5, 'Object must not have 5 entries') );Validate Unicode code points with codePoints
The
v.codePointsaction validates the number of Unicode code points in a string. This is distinct from UTF-16 code units or graphemes (e.g., an emoji like😀is 1 code point but 2 UTF-16 code units).requirement: The exact number of code points required.message: An optional custom error message.
const CodePointsSchema = v.pipe( v.string(), v.codePoints(8, 'Exactly 8 code points are required.') );Convert Zod parse methods to Valibot modular functions
In Valibot, methods likeparseandsafeParseare standalone functions rather than methods on the schema. Pass the schema as the first argument to the function.// Zod const value = z.string().parse('foo'); // Valibot const value = v.parse(v.string(), 'foo');