# Migrate from TypeBox

> This document is the Markdown version of [valibot.dev/guides/migrate-from-typebox/](https://valibot.dev/guides/migrate-from-typebox/). For the complete documentation index, see [llms.txt](https://valibot.dev/llms.txt).

Migrating from [TypeBox](https://github.com/sinclairzx81/typebox) to Valibot is straightforward in most cases since both libraries are type-safe and share the same basic concepts. Like Valibot, TypeBox schemas are required by default, which makes the structure of your schemas easy to map. The following guide will help you migrate step by step and also point out important differences.

## Replace imports

The first thing to do after [installing](/guides/installation.md) Valibot is to update your imports. Since Valibot validates your data itself, the separate imports for the `Value` module or the `TypeCompiler` are no longer needed. To infer the type of a schema, replace `Static` with [`InferOutput`](/api/InferOutput.md).

```ts
// Change this
import { Type, type Static } from '@sinclair/typebox';
import { Value } from '@sinclair/typebox/value';
const Schema = Type.Object({ key: Type.String() });
type Data = Static<typeof Schema>;

// To this
import * as v from 'valibot';
const Schema = v.object({ key: v.string() });
type Data = v.InferOutput<typeof Schema>;
```

## Restructure code

TypeBox describes constraints with a JSON Schema options object as the last argument. In Valibot you use [pipelines](/guides/pipelines.md) to do the same thing. This is a function that starts with a schema and is followed by up to 19 validation or transformation actions.

```ts
// Change this
const Schema = Type.String({ minLength: 3, maxLength: 30 });

// To this
const Schema = v.pipe(v.string(), v.minLength(3), v.maxLength(30));
```

To validate or parse data, you pass the schema as the first argument to methods like [`is`](/api/is.md), [`parse`](/api/parse.md) or [`safeParse`](/api/safeParse.md).

```ts
// Change this
const valid = Value.Check(Schema, input);

// To this
const valid = v.is(Schema, input);
```

We recommend that you read our [mental model](/guides/mental-model.md) guide to understand how the individual functions of Valibot's modular API work together.

## String formats

In TypeBox, string formats like `email` must be registered with the `FormatRegistry` before they can be used, or they will fail validation. Valibot ships these validations as actions that work out of the box.

```ts
// Change this
FormatRegistry.Set('email', (value) => isEmail(value));
const Schema = Type.String({ format: 'email' });

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

## No implicit type conversion

TypeBox's `Value.Parse` runs the `Clean`, `Default` and `Convert` operations before asserting the type. This means that it removes unknown object keys and converts values to the expected type. For example, the string `'24'` is converted to the number `24`. In Valibot, this behavior is not controlled by the parsing method, but by the schema itself. The [`object`](/api/object.md) schema removes unknown keys and [`optional`](/api/optional.md) applies default values, but no schema ever converts types implicitly. If you rely on type conversion, use a pipeline with an explicit [`transform`](/api/transform.md) action or one of the dedicated transformation actions like [`toNumber`](/api/toNumber.md) or [`toDate`](/api/toDate.md). This forces you to explicitly define the input, resulting in safer code.

```ts
// Change this
const value = Value.Parse(Type.Number(), input);

// To this
const value = v.parse(v.pipe(v.string(), v.toNumber()), input);
```

Similarly, `Type.Transform` with its `Decode` and `Encode` functions is replaced by a pipeline with a [`transform`](/api/transform.md) action. Note that Valibot only transforms in one direction and does not provide an equivalent to `Value.Encode`.

```ts
// Change this
const Schema = Type.Transform(Type.String())
  .Decode((value) => new Date(value))
  .Encode((value) => value.toISOString());
const date = Value.Decode(Schema, input);

// To this
const Schema = v.pipe(v.string(), v.toDate());
const date = v.parse(Schema, input);
```

Keep in mind that [`toNumber`](/api/toNumber.md) behaves like JavaScript's `Number` function and therefore converts empty strings to `0`, and that [`toDate`](/api/toDate.md) accepts any string that the `Date` constructor can parse. For stricter validation, we recommend adding actions like [`decimal`](/api/decimal.md) or [`isoTimestamp`](/api/isoTimestamp.md) to validate the formatting of the string before converting it.

```ts
const NumberSchema = v.pipe(v.string(), v.decimal(), v.toNumber());
const DateSchema = v.pipe(v.string(), v.isoTimestamp(), v.toDate());
```

## Change names

Many type builder functions just change to lowercase, such as `Type.String` to [`string`](/api/string.md) or `Type.Object` to [`object`](/api/object.md). However, there are some exceptions. The following table shows all names that have changed beyond that.

| TypeBox          | Valibot                                                                                      |
| ---------------- | -------------------------------------------------------------------------------------------- |
| `Static`         | [`InferOutput`](/api/InferOutput.md)                                          |
| `StaticDecode`   | [`InferOutput`](/api/InferOutput.md)                                          |
| `StaticEncode`   | [`InferInput`](/api/InferInput.md)                                            |
| `Type.Composite` | [Object merging](/guides/intersections.md#merge-objects)                           |
| `Type.Integer`   | [`number`](/api/number.md) with [`integer`](/api/integer.md)   |
| `Type.KeyOf`     | [`keyof`](/api/keyof.md)                                                      |
| `Type.Recursive` | [`lazy`](/api/lazy.md)                                                        |
| `Type.Transform` | [`pipe`](/api/pipe.md) with [`transform`](/api/transform.md)   |
| `Type.Unsafe`    | [`custom`](/api/custom.md)                                                    |
| `TypeCompiler`   | [`parser`](/api/parser.md), [`safeParser`](/api/safeParser.md) |
| `Value.Check`    | [`is`](/api/is.md)                                                            |
| `Value.Decode`   | [`parse`](/api/parse.md)                                                      |
| `Value.Default`  | [`optional`](/api/optional.md)                                                |
| `Value.Errors`   | [`safeParse`](/api/safeParse.md)                                              |
| `Value.Parse`    | [`parse`](/api/parse.md)                                                      |

The same applies to the constraint options. The following table shows how they map to Valibot's actions.

| TypeBox                       | Valibot                                                                                                                        |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `additionalProperties: false` | [`strictObject`](/api/strictObject.md)                                                                          |
| `additionalProperties: T`     | [`objectWithRest`](/api/objectWithRest.md)                                                                      |
| `default`                     | [`optional`](/api/optional.md)                                                                                  |
| `exclusiveMaximum`            | [`ltValue`](/api/ltValue.md)                                                                                    |
| `exclusiveMinimum`            | [`gtValue`](/api/gtValue.md)                                                                                    |
| `format`                      | [`email`](/api/email.md), [`url`](/api/url.md), [`uuid`](/api/uuid.md) and others |
| `maximum`                     | [`maxValue`](/api/maxValue.md)                                                                                  |
| `maxItems`                    | [`maxLength`](/api/maxLength.md)                                                                                |
| `maxProperties`               | [`maxEntries`](/api/maxEntries.md)                                                                              |
| `minimum`                     | [`minValue`](/api/minValue.md)                                                                                  |
| `minItems`                    | [`minLength`](/api/minLength.md)                                                                                |
| `minProperties`               | [`minEntries`](/api/minEntries.md)                                                                              |
| `multipleOf`                  | [`multipleOf`](/api/multipleOf.md)                                                                              |
| `pattern`                     | [`regex`](/api/regex.md)                                                                                        |
| `uniqueItems`                 | [`checkItems`](/api/checkItems.md)                                                                              |

## Other details

Below are some more details that may be helpful when migrating from TypeBox to Valibot.

### Unknown object keys

Following JSON Schema semantics, TypeBox allows and keeps unknown keys when validating objects by default. Valibot's [`object`](/api/object.md) schema removes them instead, similar to `Value.Clean`. If you rely on TypeBox's behavior, use [`looseObject`](/api/looseObject.md). To reject unknown keys, as with `additionalProperties: false`, use [`strictObject`](/api/strictObject.md). See the [objects](/guides/objects.md) guide for more details.

```ts
// Change this
const ObjectSchema = Type.Object(
  { key: Type.String() },
  { additionalProperties: false }
);

// To this
const ObjectSchema = v.strictObject({ key: v.string() });
```

### Unique items

Valibot does not provide a dedicated action for JSON Schema's `uniqueItems` keyword. Instead, you can use the [`checkItems`](/api/checkItems.md) action. Its requirement function receives the entire array as its third argument, which allows you to compare each item against the others.

```ts
// Change this
const Schema = Type.Array(Type.Number(), { uniqueItems: true });

// To this
const Schema = v.pipe(
  v.array(v.number()),
  v.checkItems(
    (item, index, array) => array.indexOf(item) === index,
    'Duplicate items are not allowed'
  )
);
```

### Reusable parsers

TypeBox's `TypeCompiler` generates optimized JavaScript code for a schema at runtime. Valibot's schemas are executed directly and do not require a compile step. This also means that Valibot does not evaluate generated code, which allows it to run in environments with a strict Content Security Policy. If you like the ergonomics of a compiled validator, [`parser`](/api/parser.md) and [`safeParser`](/api/safeParser.md) create a reusable function bound to your schema.

```ts
// Change this
const Compiled = TypeCompiler.Compile(Schema);
const valid = Compiled.Check(input);

// To this
const parseSchema = v.safeParser(Schema);
const result = parseSchema(input);
```

### JSON Schema

TypeBox schemas are JSON Schema objects. Valibot schemas are plain JavaScript objects with their own structure. If you need JSON Schema output, for example for OpenAPI definitions or LLM structured outputs, you can use the official `@valibot/to-json-schema` package to convert your Valibot schemas to JSON Schema. See the [JSON Schema](/guides/json-schema.md) guide for more details.

```ts
import { toJsonSchema } from '@valibot/to-json-schema';
import * as v from 'valibot';

const ValibotSchema = v.object({
  name: v.string(),
  email: v.pipe(v.string(), v.email()),
});

const jsonSchema = toJsonSchema(ValibotSchema);
```

### Async validation

Unlike TypeBox, Valibot also supports asynchronous validation, for example for database checks. See the [async guide](/guides/async-validation.md) for more details.
