Migrate to v0.31.0

Migrating Valibot from an older version to v0.31.0 isn't complicated. Except for the new pipe method, most things remain the same. The following guide will help you to migrate automatically or manually step by step and also point out important differences.

Automatic upgrade

We worked together with Codemod and Grit to automatically upgrade your schemas to the new version with a single CLI command. Both codemods are similar. You can use one or the other. Simply run the command in the directory of your project.

We recommend using a version control system like Git so that you can revert changes if the codemod screws something up.

# Codemod
npx codemod valibot/migrate-to-v0.31.0

# Grit
npx @getgrit/cli apply github.com/fabian-hiller/valibot#migrate_to_v0_31_0

Please create an issue if you encounter any problems or unexpected behavior with the provided codemods.

Restructure code

As mentioned above, one of the biggest differences is the new pipe method. Previously, you passed the pipeline as an array to a schema function. Now you pass the schema with various actions to the new pipe method to extend a schema.

// Change this
const Schema = v.string([v.email()]);

// To this
const Schema = v.pipe(v.string(), v.email()));

We will be publishing a blog post soon explaining all the benefits of this change. In the meantime, you can read the description of discussion #463 and PR #502, which introduced this change.

Change names

Most of the names are the same as before. However, there are some exceptions. The following table shows all names that have changed.

coercepipe, unknown and transform

Special cases

More complex schemas may require a bit more restructuring. This section provides more details on how to migrate specific functions.

Objects and tuples

Previously, you could pass a rest argument to the object and tuple schemas to define the behavior for unknown entries and items. We have removed the rest argument to simplify the implementation and reduce the bundle size if this functionality is not needed. If you do need this functionality, there is now a new objectWithRest and tupleWithRest schema.

// Change this
const ObjectSchema = v.object({ key: v.string() }, v.null_());
const TupleSchema = v.tuple([v.string()], v.null_());

// To this
const ObjectSchema = v.objectWithRest({ key: v.string() }, v.null_());
const TupleSchema = v.tupleWithRest([v.string()], v.null_());

To further improve the developer experience, we have also added a looseObject, looseTuple, strictObject and strictTuple schema. These schemas allow or disallow unknown entries or items.

// Change this
const LooseObjectSchema = v.object({ key: v.string() }, v.unknown());
const LooseTupleSchema = v.tuple([v.string()], v.unknown());
const StrictObjectSchema = v.object({ key: v.string() }, v.never());
const StrictTupleSchema = v.tuple([v.string()], v.never());

// To this
const LooseObjectSchema = v.looseObject({ key: v.string() });
const LooseTupleSchema = v.looseTuple([v.string()]);
const StrictObjectSchema = v.strictObject({ key: v.string() });
const StrictTupleSchema = v.strictTuple([v.string()]);

Object merging

Since there are now 4 different object schemas, we could no longer provide a simple merge function that works in all cases, as we never know which schema you want to merge the other objects into. But there is a simple workaround with a similar developer experience.

const ObjectSchema1 = v.object({ foo: v.string() });
const ObjectSchema2 = v.object({ bar: v.number() });

// Change this
const MergedObject = v.merge([ObjectSchema1, ObjectSchema2]);

// To this
const MergedObject = v.object({

Brand and transform

Previously, brand and transform were methods that could be wrapped around a schema to modify it. With our new pipe method, this is no longer necessary. Instead, brand and transform are now transformation actions that can be placed directly in a pipeline, resulting in better readability, especially for complex schemas.

// Change this
const BrandedSchema = v.brand(v.string(), 'foo');
const TransformedSchema = v.transform(v.string(), (input) => input.length);

// To this
const BrandedSchema = v.pipe(v.string(), v.brand('foo'));
const TransformedSchema = v.pipe(
  v.transform((input) => input.length)

Coerce method

The coerce method has been removed because we felt it was an insecure API. In most cases, you don't want to coerce an unknown input into a specific data type. Instead, you want to transform a specific data type into another specific data type. For example, a string or a number into a date. To explicitly define the input type, we recommend using the new pipe method together with the transform action to achieve the same functionality.

// Change this
const DateSchema = v.coerce(v.date(), (input) => new Date(input));

// To this
const DateSchema = v.pipe(
  v.union([v.string(), v.number()]),
  v.transform((input) => new Date(input))

Flatten issues

Previously, the flatten function accepted a ValiError or an array of issues. We have simplified the implementation by only allowing an array of issues to be passed.

// Change this
const flatErrors = v.flatten(error);

// To this
const flatErrors = v.flatten(error.issues);


Thanks to all the contributors who helped make this page better!

  • GitHub profile picture of fabian-hiller
  • GitHub profile picture of lesleh


Thanks to our partners who support the project ideally and financially.


Thanks to our GitHub sponsors who support the project financially.

  • GitHub profile picture of dailydotdev
  • GitHub profile picture of KATT
  • GitHub profile picture of osdiab
  • GitHub profile picture of Thanaen
  • GitHub profile picture of ruiaraujo012
  • GitHub profile picture of hyunbinseo
  • GitHub profile picture of caegdeveloper
  • GitHub profile picture of seahindeniz