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.

Tokens
75.6K
Snippets
490
Records
608
Agent score
73%

What's inside Valibot

  1. Overview of Valibot schema validation

    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.
  2. 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 BaseSchema that validate a specific data type. They include properties like kind: 'schema', type (snake_case), and a ~run function for parsing datasets.
    • Validation Actions: Objects satisfying BaseValidation that check a typed value and may add issues (e.g., min_length).
    • Transformation Actions: Objects satisfying BaseTransformation that convert a value to a new type or value (e.g., trim).
  3. 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 by minLength.
    • path (Optional): An array of PathItem objects describing the location of the issue within complex data structures.
    • issues (Optional): Used specifically with union schemas to contain all issues from the union's member schemas.
    • config: Contains configuration state like lang, abortEarly, and abortPipeEarly.
  4. Assign metadata titles to schemas with v.title

    The v.title action 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...')
    );
  5. Merge object schemas in v0.31.0

    The v.merge() function has been removed because of the introduction of multiple object schema types (loose, strict, etc.). To merge objects, manually spread the entries property of the schemas into a new v.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,
    });
  6. Understand the Valibot mental model

    Valibot's modular API is built around three core concepts:

    1. Schemas: The starting point for validation. They define a specific data type (e.g., string, object) and can be nested to create complex structures.
    2. 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)).
    3. Actions: Functions used to further validate, transform, or annotate data. Actions are used exclusively within a v.pipe() to extend a schema's functionality.
  7. Handle validation issues using BaseIssue

    When a schema or action fails, it adds a BaseIssue object 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. BaseIssue also inherits from Config to 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
    }
  8. 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 to v.pipe() following the initial schema. Methods like .validate() are replaced by standalone functions like v.safeParse() or v.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');
  9. 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 Function constructor.
  10. Validate that an object does not have a specific number of entries using v.notEntries

    The v.notEntries action validates that the number of entries in an object (such as a v.record or v.object) does not equal the specified requirement.
    const NotEntriesSchema = v.pipe(
      v.record(v.string(), v.number()),
      v.notEntries(5, 'Object must not have 5 entries')
    );
  11. Validate Unicode code points with codePoints

    The v.codePoints action 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.')
    );
  12. Convert Zod parse methods to Valibot modular functions

    In Valibot, methods like parse and safeParse are 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');