It often happens that undefined or null should also be accepted instead of the value. To make the API more readable for this and to reduce boilerplate, I offer a shortcut for this functionality with optional, nullable and nullish.

How it works

To accept undefined or null besides your actual value, you just have to nest the schema in optional, nullable and nullish.

import * as v from 'valibot';

const OptionalStringSchema = v.optional(v.string()); // string | undefined
const NullableStringSchema = v.nullable(v.string()); // string | null
const NullishStringSchema = v.nullish(v.string()); // string | null | undefined

Use in objects

When used inside of objects, optional is a special case, as it also marks the value as optional in TypeScript with a question mark.

import * as v from 'valibot';

const OptionalKeySchema = v.object({ key: v.optional(v.string()) }); // { key?: string | undefined }

It is important to note that nullish does not behave this way. To make a nullable value optional, you must explicitly nest it in optional.

Let us know with an issue if you find this cumbersome and would prefer nullish to do this automatically.

import * as v from 'valibot';

const NullishKeySchema = v.object({
  key: v.nullish(v.string()),
}); // { key: string | null | undefined }

const OptionalNullableKeySchema = v.object({
  key: v.optional(v.nullable(v.string())),
}); // { key?: string | null | undefined }

Default values

The special thing about optional, nullable and nullish is that the schema functions accept a default value as the second argument. Depending on the schema function, this default value is always used if the input is undefined or null.

import * as v from 'valibot';

const OptionalStringSchema = v.optional(v.string(), "I'm the default!");

type OptionalStringInput = v.Input<typeof OptionalStringSchema>; // string | undefined
type OptionalStringOutput = v.Output<typeof OptionalStringSchema>; // string

By providing a default value, the input type of the schema now differs from the output type. The schema in the example now accepts string and undefined as input, but returns a string as output in both cases.

Dynamic values

In some cases it is necessary to generate the default value dynamically. For this purpose, a function that generates and returns the default value can also be passed as the second argument.

import * as v from 'valibot';

const NullableDateSchema = v.nullable(v.date(), () => new Date());

The previous example thus creates a new instance of the Date class for each validation with null as input, which is then used as the default value.


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

  • GitHub profile picture of fabian-hiller


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 ivan-mihalic
  • GitHub profile picture of KATT
  • GitHub profile picture of osdiab
  • GitHub profile picture of Thanaen
  • GitHub profile picture of hyunbinseo
  • GitHub profile picture of caegdeveloper