@valibot/to-json-schema
@valibot/to-json-schema is Valibot's official converter from executable Valibot schemas to portable JSON Schema. It emits draft-07 by default, can target draft-2020-12 or OpenAPI 3.0 Schema Objects, and supports reusable definitions, recursive lazy schemas, metadata, input-versus-output conversion, and custom overrides. It translates structure and many validations, but it does not turn JavaScript transformations or every Valibot-specific rule into equivalent cross-language behavior.
The natural converter for a Valibot codebase, with strong target, definition, and override support and unusually honest compatibility documentation. Keep default throw behavior, choose input or output mode deliberately, and test generated schemas because no converter can preserve JavaScript transformations or every validator's semantics.
Use it if
- Valibot is your runtime validator and you need JSON Schema for documentation, code generation, forms, or structured model output
- You need draft-07, draft-2020-12, or OpenAPI 3.0 output from one schema source
- You want named definitions and custom reference paths for OpenAPI components
- You are prepared to test generated schemas in the exact downstream validator or API consumer
- Your Valibot pipeline relies on transformations or JavaScript-only refinements: the README states that transformations have no JSON Schema equivalent and unsupported features may throw or disappear
- You need identical validation semantics across runtimes: the docs warn that string formats can differ and Valibot counts JavaScript UTF-16 code units while JSON Schema length counts Unicode code points
- You need a direct OpenAPI 3.1 target: the converter offers OpenAPI 3.0 plus JSON Schema drafts, and its documented 3.0 target omits unsupported features such as record `propertyNames`
- You use an older Valibot release: version 1.7.1 declares Valibot ^1.4.0 as a peer, so mismatched schema object shapes are outside its supported range
- You intend to set `errorMode: 'ignore'` and treat output as complete: unsupported schemas commonly become `{}` and unsupported actions are ignored, which broadens accepted data without an obvious failure
Setup reality
Install both packages with npm install valibot @valibot/to-json-schema. Converter 1.7.1 has no runtime dependencies, native build, credentials, or config file, but it requires Valibot ^1.4.0 as a peer. It ships ESM and CommonJS exports, TypeScript declarations, and `sideEffects: false`. `toJsonSchema(schema)` runs synchronously and defaults to draft-07; choose `target` explicitly when another tool expects draft-2020-12 or OpenAPI 3.0. Conversion is not proof of behavioral equivalence. Transform actions have no JSON Schema representation, many JavaScript-specific schemas and validations are unsupported, regex flags cannot be carried over, variant discriminator keys are ignored, and JSON Schema validators may implement formats differently. Unicode length is a particularly real trap: Valibot uses JavaScript string length in UTF-16 code units while JSON Schema counts code points. By default incompatible features throw. `errorMode: 'warn'` or `'ignore'` can keep generation moving, but ignore commonly emits an empty schema or drops an action, which accepts more than the Valibot source. Prefer a deliberate `overrideSchema` or `overrideAction` and regression-test generated output. For transforming pipelines, set `typeMode: 'input'` for request shape or `'output'` for response shape; output mode needs an explicit schema after the last transformation. Named `definitions` make stable references, while lazy schemas can generate numeric definition IDs automatically. `overrideRef` is needed when embedding definitions under an OpenAPI components path. The beta override, type-mode, and global-definition APIs deserve extra upgrade tests. Global definitions are stored in module-level mutable state and have no documented clearing function, so per-call definitions are safer in servers and test suites.
Patterns
Convert a Valibot schema to draft-07convert-basic-schema
import {toJsonSchema} from '@valibot/to-json-schema';
import * as v from 'valibot';
const schema = toJsonSchema(v.string());
console.log(schema.$schema, schema.type);draft-07 is the default. Set target explicitly when another draft or OpenAPI is required.
Convert required and optional object fieldsconvert-object-schema
const UserSchema = v.object({
id: v.pipe(v.string(), v.uuid()),
name: v.pipe(v.string(), v.minLength(1)),
age: v.optional(v.pipe(v.number(), v.minValue(0))),
});
const jsonSchema = toJsonSchema(UserSchema);Only non-optional properties appear in the generated required array.
Generate JSON Schema draft 2020-12target-draft-2020-12
const jsonSchema = toJsonSchema(UserSchema, {
target: 'draft-2020-12',
});The generated $schema URI changes, but the downstream validator must also support draft 2020-12.
Generate an OpenAPI 3.0 Schema Objecttarget-openapi-3
const openApiSchema = toJsonSchema(
v.nullable(v.string()),
{target: 'openapi-3.0'},
);
// {type: 'string', nullable: true}OpenAPI 3.0 output has no $schema property and cannot express every JSON Schema feature, including propertyNames.
Carry title, description, and examplesinclude-schema-metadata
const EmailSchema = v.pipe(
v.string(),
v.email(),
v.metadata({
title: 'Email',
description: 'Primary contact address',
examples: ['jane@example.com'],
}),
);
const jsonSchema = toJsonSchema(EmailSchema);Only valid title, description, and examples metadata is converted.
Create stable reusable definitionscreate-named-definitions
const EmailSchema = v.pipe(v.string(), v.email());
const AccountSchema = v.object({email: EmailSchema});
const jsonSchema = toJsonSchema(AccountSchema, {
definitions: {EmailSchema},
});Passing the same schema object in definitions lets occurrences become references instead of repeated inline schemas.
Convert a recursive lazy schemaconvert-recursive-schema
const CategorySchema = v.object({
name: v.string(),
children: v.array(v.lazy(() => CategorySchema)),
});
const jsonSchema = toJsonSchema(CategorySchema);The lazy getter is executed with undefined as input, and automatic definitions may receive generated numeric IDs.
Generate the accepted input side of a transformdescribe-transform-input
const AmountSchema = v.pipe(
v.string(),
v.decimal(),
v.transform(Number),
v.number(),
v.maxValue(100),
);
const inputSchema = toJsonSchema(AmountSchema, {typeMode: 'input'});Input mode stops before the first possible type transformation or second schema in a pipeline.
Generate the output side of a transformdescribe-transform-output
const outputSchema = toJsonSchema(AmountSchema, {
typeMode: 'output',
});Output mode starts from the last schema; put an explicit schema after the final transform or the output cannot be described safely.
Map a Valibot File to OpenAPI binary inputoverride-unsupported-schema
const UploadSchema = v.object({file: v.file()});
const schema = toJsonSchema(UploadSchema, {
target: 'openapi-3.0',
overrideSchema({valibotSchema}) {
if (valibotSchema.type === 'file') {
return {type: 'string', format: 'binary'};
}
},
});An override suppresses conversion errors for the matched schema. Confirm that your OpenAPI consumer uses the same representation.
Generate schemas for OpenAPI componentsbuild-openapi-components
import {toJsonSchemaDefs} from '@valibot/to-json-schema';
const components = toJsonSchemaDefs(
{Email: EmailSchema, User: UserSchema},
{
target: 'openapi-3.0',
overrideRef: ({referenceId}) => `#/components/schemas/${referenceId}`,
},
);toJsonSchemaDefs returns only the definitions map; insert it under components.schemas yourself.
Keep unsupported conversion failures visiblehandle-conversion-errors
try {
const schema = toJsonSchema(v.file());
publish(schema);
} catch (error) {
console.error('Schema conversion failed', error);
process.exitCode = 1;
}Throw is the default and safest CI behavior. Ignore mode can turn unsupported schemas into {}, accepting any value.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| zod-to-json-schema | npm | Your existing schemas are Zod 3 and you need its established conversion options |
| @alcyone-labs/zod-to-json-schema | npm | You use Zod 4 and want a maintained fork focused on converting it |
| @sinclair/typebox | npm | You want to author JSON Schema objects directly while retaining static TypeScript inference |
| arktype | npm | You prefer ArkType's runtime and type syntax and can use its own schema interoperability path |