1 line
44 KiB
JSON
1 line
44 KiB
JSON
{"success":true,"data":{"web":[{"url":"https://zod.dev/json-schema","title":"JSON Schema - Zod","description":"To convert a Zod schema to JSON Schema, use the z.toJSONSchema() function. ... All schema & checks are converted to their closest JSON Schema equivalent. Some ...","position":1,"markdown":"💎 Zod 4 is now stable! [Read the announcement.](https://zod.dev/v4)\n\nAsk AI\n\n\n# JSON Schema\n\nCopy markdown\n[Edit this page](https://github.com/colinhacks/zod/edit/main/packages/docs/content/json-schema.mdx)\n\n💎\n\n**New** — Zod 4 introduces a new feature: native [JSON Schema](https://json-schema.org/) conversion. JSON Schema is a standard for describing the structure of JSON (with JSON). It's widely used in [OpenAPI](https://www.openapis.org/) definitions and defining [structured outputs](https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat) for AI.\n\n## [`z.fromJSONSchema()`](https://zod.dev/json-schema?id=zfromjsonschema)\n\n**Experimental** — The `z.fromJSONSchema()` function is experimental and is not considered part of Zod's stable API. It is likely to undergo implementation changes in future releases.\n\nZod provides `z.fromJSONSchema()` to convert a JSON Schema into a Zod schema.\n\n```\nimport * as z from \"zod\";\n\nconst jsonSchema = {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n age: { type: \"number\" },\n },\n required: [\"name\", \"age\"],\n};\n\nconst zodSchema = z.fromJSONSchema(jsonSchema);\n```\n\n## [`z.toJSONSchema()`](https://zod.dev/json-schema?id=ztojsonschema)\n\nTo convert a Zod schema to JSON Schema, use the `z.toJSONSchema()` function.\n\n```\nimport * as z from \"zod\";\n\nconst schema = z.object({\n name: z.string(),\n age: z.number(),\n});\n\nz.toJSONSchema(schema)\n// => {\n// type: 'object',\n// properties: { name: { type: 'string' }, age: { type: 'number' } },\n// required: [ 'name', 'age' ],\n// additionalProperties: false,\n// }\n```\n\nAll schema & checks are converted to their closest JSON Schema equivalent. Some types have no analog and cannot be reasonably represented. See the [`unrepresentable`](https://zod.dev/json-schema#unrepresentable) section below for more information on handling these cases.\n\n```\nz.bigint(); // ❌\nz.int64(); // ❌\nz.symbol(); // ❌\nz.undefined(); // ❌\nz.void(); // ❌\nz.date(); // ❌\nz.map(); // ❌\nz.set(); // ❌\nz.transform(); // ❌\nz.nan(); // ❌\nz.custom(); // ❌\n```\n\nA second argument can be used to customize the conversion logic.\n\n```\nz.toJSONSchema(schema, {\n // ...params\n})\n```\n\nBelow is a quick reference for each supported parameter. Each one is explained in more detail below.\n\n```\ninterface ToJSONSchemaParams {\n /** The JSON Schema version to target.\n * - `\"draft-2020-12\"` — Default. JSON Schema Draft 2020-12\n * - `\"draft-07\"` — JSON Schema Draft 7\n * - `\"draft-04\"` — JSON Schema Draft 4\n * - `\"openapi-3.0\"` — OpenAPI 3.0 Schema Object */\n target?:\n | \"draft-04\"\n | \"draft-4\"\n | \"draft-07\"\n | \"draft-7\"\n | \"draft-2020-12\"\n | \"openapi-3.0\"\n | ({} & string)\n | undefined;\n\n /** A registry used to look up metadata for each schema.\n * Any schema with an `id` property will be extracted as a $def. */\n metadata?: $ZodRegistry<Record<string, any>>;\n\n /** How to handle unrepresentable types.\n * - `\"throw\"` — Default. Unrepresentable types throw an error\n * - `\"any\"` — Unrepresentable types become `{}` */\n unrepresentable?: \"throw\" | \"any\";\n\n /** How to handle cycles.\n * - `\"ref\"` — Default. Cycles will be broken using $defs\n * - `\"throw\"` — Cycles will throw an error if encountered */\n cycles?: \"ref\" | \"throw\";\n\n /* How to handle reused schemas.\n * - `\"inline\"` — Default. Reused schemas will be inlined\n * - `\"ref\"` — Reused schemas will be extracted as $defs */\n reused?: \"ref\" | \"inline\";\n\n /** A function used to convert `id` values to URIs to be used in *external* $refs.\n *\n * Default is `(id) => id`.\n */\n uri?: (id: string) => string;\n}\n```\n\n### [`io`](https://zod.dev/json-schema?id=io)\n\nSome schema types have different input and output types, e.g. `ZodPipe`, `ZodDefault`, and coerced primitives. By default, the result of `z.toJSONSchema` represents the _output type_; use `\"io\": \"input\"` to extract the input type instead.\n\n```\nconst mySchema = z.string().transform(val => val.length).pipe(z.number());\n// ZodPipe\n\nconst jsonSchema = z.toJSONSchema(mySchema);\n// => { type: \"number\" }\n\nconst jsonSchema = z.toJSONSchema(mySchema, { io: \"input\" });\n// => { type: \"string\" }\n```\n\n### [`target`](https://zod.dev/json-schema?id=target)\n\nTo set the target JSON Schema version, use the `target` parameter. By default, Zod will target Draft 2020-12.\n\n```\nz.toJSONSchema(schema, { target: \"draft-07\" });\nz.toJSONSchema(schema, { target: \"draft-2020-12\" });\nz.toJSONSchema(schema, { target: \"draft-04\" });\nz.toJSONSchema(schema, { target: \"openapi-3.0\" });\n```\n\n### [`metadata`](https://zod.dev/json-schema?id=metadata)\n\nIf you haven't already, read through the [Metadata and registries](https://zod.dev/metadata) page for context on storing metadata in Zod.\n\nIn Zod, metadata is stored in registries. Zod exports a global registry `z.globalRegistry` that can be used to store common metadata fields like `id`, `title`, `description`, and `examples`.\n\nZodZod Mini\n\n```\nimport * as z from \"zod\";\n\n// `.meta()` is a convenience method for registering a schema in `z.globalRegistry`\nconst emailSchema = z.string().meta({\n title: \"Email address\",\n description: \"Your email address\",\n});\n\nz.toJSONSchema(emailSchema);\n// => { type: \"string\", title: \"Email address\", description: \"Your email address\", ... }\n```\n\nAll metadata fields get copied into the resulting JSON Schema.\n\n```\nconst schema = z.string().meta({\n whatever: 1234\n});\n\nz.toJSONSchema(schema);\n// => { type: \"string\", whatever: 1234 }\n```\n\n### [`unrepresentable`](https://zod.dev/json-schema?id=unrepresentable)\n\nThe following APIs are not representable in JSON Schema. By default, Zod will throw an error if they are encountered. It is unsound to attempt a conversion to JSON Schema; you should modify your schemas as they have no equivalent in JSON. An error will be thrown if any of these are encountered.\n\n```\nz.bigint(); // ❌\nz.int64(); // ❌\nz.symbol(); // ❌\nz.undefined(); // ❌\nz.void(); // ❌\nz.date(); // ❌\nz.map(); // ❌\nz.set(); // ❌\nz.transform(); // ❌\nz.nan(); // ❌\nz.custom(); // ❌\n```\n\nBy default, Zod will throw an error if any of these are encountered.\n\n```\nz.toJSONSchema(z.bigint());\n// => throws Error\n```\n\nYou can change this behavior by setting the `unrepresentable` option to `\"any\"`. This will convert any unrepresentable types to `{}` (the equivalent of `unknown` in JSON Schema).\n\n```\nz.toJSONSchema(z.bigint(), { unrepresentable: \"any\" });\n// => {}\n```\n\n### [`cycles`](https://zod.dev/json-schema?id=cycles)\n\nHow to handle cycles. If a cycle is encountered as `z.toJSONSchema()` traverses the schema, it will be represented using `$ref`.\n\n```\nconst User = z.object({\n name: z.string(),\n get friend() {\n return User;\n },\n});\n\nz.toJSONSchema(User);\n// => {\n// type: 'object',\n// properties: { name: { type: 'string' }, friend: { '$ref': '#' } },\n// required: [ 'name', 'friend' ],\n// additionalProperties: false,\n// }\n```\n\nIf instead you want to throw an error, set the `cycles` option to `\"throw\"`.\n\n```\nz.toJSONSchema(User, { cycles: \"throw\" });\n// => throws Error\n```\n\n### [`reused`](https://zod.dev/json-schema?id=reused)\n\nHow to handle schemas that occur multiple times in the same schema. By default, Zod will inline these schemas.\n\n```\nconst name = z.string();\nconst User = z.object({\n firstName: name,\n lastName: name,\n});\n\nz.toJSONSchema(User);\n// => {\n// type: 'object',\n// properties: {\n// firstName: { type: 'string' },\n// lastName: { type: 'string' }\n// },\n// required: [ 'firstName', 'lastName' ],\n// additionalProperties: false,\n// }\n```\n\nInstead you can set the `reused` option to `\"ref\"` to extract these schemas into `$defs`.\n\n```\nz.toJSONSchema(User, { reused: \"ref\" });\n// => {\n// type: 'object',\n// properties: {\n// firstName: { '$ref': '#/$defs/__schema0' },\n// lastName: { '$ref': '#/$defs/__schema0' }\n// },\n// required: [ 'firstName', 'lastName' ],\n// additionalProperties: false,\n// '$defs': { __schema0: { type: 'string' } }\n// }\n```\n\n### [`override`](https://zod.dev/json-schema?id=override)\n\nTo define some custom override logic, use `override`. The provided callback has access to the original Zod schema and the default JSON Schema. _This function should directly modify `ctx.jsonSchema`._\n\n```\nconst mySchema = /* ... */\nz.toJSONSchema(mySchema, {\n override: (ctx)=>{\n ctx.zodSchema; // the original Zod schema\n ctx.jsonSchema; // the default JSON Schema\n\n // directly modify\n ctx.jsonSchema.whatever = \"sup\";\n }\n});\n```\n\nNote that unrepresentable types will throw an `Error` before this function is called. If you are trying to define custom behavior for an unrepresentable type, you'll need to set the `unrepresentable: \"any\"` alongside `override`.\n\n```\n// support z.date() as ISO datetime strings\nconst result = z.toJSONSchema(z.date(), {\n unrepresentable: \"any\",\n override: (ctx) => {\n const def = ctx.zodSchema._zod.def;\n if(def.type ===\"date\"){\n ctx.jsonSchema.type = \"string\";\n ctx.jsonSchema.format = \"date-time\";\n }\n },\n});\n```\n\n## [Conversion](https://zod.dev/json-schema?id=conversion)\n\nBelow are additional details regarding Zod's JSON Schema conversion logic.\n\n### [String formats](https://zod.dev/json-schema?id=string-formats)\n\nZod converts the following schema types to the equivalent JSON Schema `format`:\n\n```\n// Supported via `format`\nz.email(); // => { type: \"string\", format: \"email\" }\nz.iso.datetime(); // => { type: \"string\", format: \"date-time\" }\nz.iso.date(); // => { type: \"string\", format: \"date\" }\nz.iso.time(); // => { type: \"string\", format: \"time\" }\nz.iso.duration(); // => { type: \"string\", format: \"duration\" }\nz.ipv4(); // => { type: \"string\", format: \"ipv4\" }\nz.ipv6(); // => { type: \"string\", format: \"ipv6\" }\nz.uuid(); // => { type: \"string\", format: \"uuid\" }\nz.guid(); // => { type: \"string\", format: \"uuid\" }\nz.url(); // => { type: \"string\", format: \"uri\" }\n```\n\nThese schemas are supported via `contentEncoding`:\n\n```\nz.base64(); // => { type: \"string\", contentEncoding: \"base64\" }\n```\n\nAll other string formats are supported via `pattern`:\n\n```\nz.base64url();\nz.cuid();\nz.emoji();\nz.nanoid();\nz.cuid2();\nz.ulid();\nz.cidrv4();\nz.cidrv6();\nz.mac();\n```\n\n### [Numeric types](https://zod.dev/json-schema?id=numeric-types)\n\nZod converts the following numeric types to JSON Schema:\n\n```\n// number\nz.number(); // => { type: \"number\" }\nz.float32(); // => { type: \"number\", exclusiveMinimum: ..., exclusiveMaximum: ... }\nz.float64(); // => { type: \"number\", exclusiveMinimum: ..., exclusiveMaximum: ... }\n\n// integer\nz.int(); // => { type: \"integer\" }\nz.int32(); // => { type: \"integer\", exclusiveMinimum: ..., exclusiveMaximum: ... }\n```\n\n### [Object schemas](https://zod.dev/json-schema?id=object-schemas)\n\nBy default, `z.object()` schemas contain `additionalProperties: \"false\"`. This is an accurate representation of Zod's default behavior, as plain `z.object()` schema strip additional properties.\n\n```\nimport * as z from \"zod\";\n\nconst schema = z.object({\n name: z.string(),\n age: z.number(),\n});\n\nz.toJSONSchema(schema)\n// => {\n// type: 'object',\n// properties: { name: { type: 'string' }, age: { type: 'number' } },\n// required: [ 'name', 'age' ],\n// additionalProperties: false,\n// }\n```\n\nWhen converting to JSON Schema in `\"input\"` mode, `additionalProperties` is not set. See the [`io` docs](https://zod.dev/json-schema#io) for more information.\n\n```\nimport * as z from \"zod\";\n\nconst schema = z.object({\n name: z.string(),\n age: z.number(),\n});\n\nz.toJSONSchema(schema, { io: \"input\" });\n// => {\n// type: 'object',\n// properties: { name: { type: 'string' }, age: { type: 'number' } },\n// required: [ 'name', 'age' ],\n// }\n```\n\nBy contrast:\n\n- `z.looseObject()` will _never_ set `additionalProperties: false`\n- `z.strictObject()` will _always_ set `additionalProperties: false`\n\n### [File schemas](https://zod.dev/json-schema?id=file-schemas)\n\nZod converts `z.file()` to the following OpenAPI-friendly schema:\n\n```\nz.file();\n// => { type: \"string\", format: \"binary\", contentEncoding: \"binary\" }\n```\n\nSize and MIME checks are also represented:\n\n```\nz.file().min(1).max(1024 * 1024).mime(\"image/png\");\n// => {\n// type: \"string\",\n// format: \"binary\",\n// contentEncoding: \"binary\",\n// contentMediaType: \"image/png\",\n// minLength: 1,\n// maxLength: 1048576,\n// }\n```\n\n### [Nullability](https://zod.dev/json-schema?id=nullability)\n\nZod converts `z.null()` to `{ type: \"null\" }` in JSON Schema.\n\n```\nz.null();\n// => { type: \"null\" }\n```\n\nNote that `z.undefined()` is unrepresentable in JSON Schema (see [below](https://zod.dev/json-schema#unrepresentable)).\n\nSimilarly, `nullable` is represented via a union with `null`:\n\n```\nz.nullable(z.string());\n// => { oneOf: [{ type: \"string\" }, { type: \"null\" }] }\n```\n\nOptional schemas are represented as-is, though they are decorated with an `optional` annotation.\n\n```\nz.optional(z.string());\n// => { type: \"string\" }\n```\n\n## [Registries](https://zod.dev/json-schema?id=registries)\n\nPassing a schema into `z.toJSONSchema()` will return a _self-contained_ JSON Schema.\n\nIn other cases, you may have a set of Zod schemas you'd like to represent using multiple interlinked JSON Schemas, perhaps to write to `.json` files and serve from a web server.\n\n```\nimport * as z from \"zod\";\n\nconst User = z.object({\n name: z.string(),\n get posts(){\n return z.array(Post);\n }\n});\n\nconst Post = z.object({\n title: z.string(),\n content: z.string(),\n get author(){\n return User;\n }\n});\n\nz.globalRegistry.add(User, {id: \"User\"});\nz.globalRegistry.add(Post, {id: \"Post\"});\n```\n\nTo achieve this, you can pass a [registry](https://zod.dev/metadata#registries) into `z.toJSONSchema()`.\n\n**Important** — All schemas should have a registered `id` property in the registry! Any schemas without an `id` will be ignored.\n\n```\nz.toJSONSchema(z.globalRegistry);\n// => {\n// schemas: {\n// User: {\n// id: 'User',\n// type: 'object',\n// properties: {\n// name: { type: 'string' },\n// posts: { type: 'array', items: { '$ref': 'Post' } }\n// },\n// required: [ 'name', 'posts' ],\n// additionalProperties: false,\n// },\n// Post: {\n// id: 'Post',\n// type: 'object',\n// properties: {\n// title: { type: 'string' },\n// content: { type: 'string' },\n// author: { '$ref': 'User' }\n// },\n// required: [ 'title', 'content', 'author' ],\n// additionalProperties: false,\n// }\n// }\n// }\n```\n\nBy default, the `$ref` URIs are simple relative paths like `\"User\"`. To make these absolute URIs, use the `uri` option. This expects a function that converts an `id` to a fully-qualified URI.\n\n```\nz.toJSONSchema(z.globalRegistry, {\n uri: (id) => `https://example.com/${id}.json`\n});\n// => {\n// schemas: {\n// User: {\n// id: 'User',\n// type: 'object',\n// properties: {\n// name: { type: 'string' },\n// posts: {\n// type: 'array',\n// items: { '$ref': 'https://example.com/Post.json' }\n// }\n// },\n// required: [ 'name', 'posts' ],\n// additionalProperties: false,\n// },\n// Post: {\n// id: 'Post',\n// type: 'object',\n// properties: {\n// title: { type: 'string' },\n// content: { type: 'string' },\n// author: { '$ref': 'https://example.com/User.json' }\n// },\n// required: [ 'title', 'content', 'author' ],\n// additionalProperties: false,\n// }\n// }\n// }\n```\n\n[Metadata and registries\\\\\n\\\\\nAttaching and manipulatinvg metadata on Zod schemas](https://zod.dev/metadata) [Codecs\\\\\n\\\\\nBidirectional transformations with encode and decode](https://zod.dev/codecs)\n\n### On this page\n\n[`z.fromJSONSchema()`](https://zod.dev/json-schema#zfromjsonschema) [`z.toJSONSchema()`](https://zod.dev/json-schema#ztojsonschema) [`io`](https://zod.dev/json-schema#io) [`target`](https://zod.dev/json-schema#target) [`metadata`](https://zod.dev/json-schema#metadata) [`unrepresentable`](https://zod.dev/json-schema#unrepresentable) [`cycles`](https://zod.dev/json-schema#cycles) [`reused`](https://zod.dev/json-schema#reused) [`override`](https://zod.dev/json-schema#override) [Conversion](https://zod.dev/json-schema#conversion) [String formats](https://zod.dev/json-schema#string-formats) [Numeric types](https://zod.dev/json-schema#numeric-types) [Object schemas](https://zod.dev/json-schema#object-schemas) [File schemas](https://zod.dev/json-schema#file-schemas) [Nullability](https://zod.dev/json-schema#nullability) [Registries](https://zod.dev/json-schema#registries)","metadata":{"ogSiteName":"Zod","title":"JSON Schema | Zod","twitter:image:height":"630","description":"How to convert Zod schemas to JSON Schema","ogDescription":"How to convert Zod schemas to JSON Schema","twitter:title":"JSON Schema | Zod","og:type":"website","twitter:site":"@colinhacks","language":"en","og:image":"https://zod.dev/og.png?title=JSON%20Schema&description=How%20to%20convert%20Zod%20schemas%20to%20JSON%20Schema&path=zod.dev%2Fjson-schema","twitter:image":"https://zod.dev/og.png?title=JSON%20Schema&description=How%20to%20convert%20Zod%20schemas%20to%20JSON%20Schema&path=zod.dev%2Fjson-schema","og:image:width":"1200","ogUrl":"https://zod.dev/json-schema","twitter:image:alt":"JSON Schema | Zod","og:image:alt":"JSON Schema | Zod","twitter:card":"summary_large_image","twitter:description":"How to convert Zod schemas to JSON Schema","twitter:image:width":"1200","og:url":"https://zod.dev/json-schema","og:description":"How to convert Zod schemas to JSON Schema","og:image:height":"630","viewport":"width=device-width, initial-scale=1","og:title":"JSON Schema | Zod","next-size-adjust":"","keywords":"zod,typescript,validation,schema","twitter:creator":"@colinhacks","ogImage":"https://zod.dev/og.png?title=JSON%20Schema&description=How%20to%20convert%20Zod%20schemas%20to%20JSON%20Schema&path=zod.dev%2Fjson-schema","og:site_name":"Zod","ogTitle":"JSON Schema | Zod","favicon":"https://zod.dev/icon.png?39fe259ddd7f4224","scrapeId":"019d3958-051d-7417-9498-6e463a9be09a","sourceURL":"https://zod.dev/json-schema","url":"https://zod.dev/json-schema","statusCode":200,"contentType":"text/html; charset=utf-8","proxyUsed":"basic","cacheState":"hit","cachedAt":"2026-03-28T14:32:59.530Z","creditsUsed":1}},{"url":"https://github.com/colinhacks/zod/discussions/4927","title":"Add descriptions to schemas for conversion to JSON Schema #4927","description":"I'm excited about the newly added toJSONSchema() function to turn a Zod schema into a JSON schema. One critical feature I'm wondering about is whether it's ...","position":2,"category":"github","markdown":"[Skip to content](https://github.com/colinhacks/zod/discussions/4927#start-of-content)\n\nYou signed in with another tab or window. [Reload](https://github.com/colinhacks/zod/discussions/4927) to refresh your session.You signed out in another tab or window. [Reload](https://github.com/colinhacks/zod/discussions/4927) to refresh your session.You switched accounts on another tab or window. [Reload](https://github.com/colinhacks/zod/discussions/4927) to refresh your session.Dismiss alert\n\n{{ message }}\n\n[colinhacks](https://github.com/colinhacks)/ **[zod](https://github.com/colinhacks/zod)** Public\n\n- [Sponsor](https://github.com/sponsors/colinhacks)\n- [Notifications](https://github.com/login?return_to=%2Fcolinhacks%2Fzod) You must be signed in to change notification settings\n- [Fork\\\\\n1.9k](https://github.com/login?return_to=%2Fcolinhacks%2Fzod)\n- [Star\\\\\n42.2k](https://github.com/login?return_to=%2Fcolinhacks%2Fzod)\n\n\n# Add descriptions to schemas for conversion to JSON Schema \\#4927\n\n[Answered](https://github.com/colinhacks/zod/discussions/4927#discussioncomment-13818620) by [dosubot](https://github.com/apps/dosubot \"dosubot[bot]\") bot\n\n[flekschas](https://github.com/flekschas)\n\nasked this question in\n[Q&A](https://github.com/colinhacks/zod/discussions/categories/q-a)\n\n[Add descriptions to schemas for conversion to JSON Schema](https://github.com/colinhacks/zod/discussions/4927#top)#4927\n\n[\\\\\nflekschas](https://github.com/flekschas)\n\non Jul 19, 2025Jul 19, 2025·\n1 comments\n·\n1 reply\n\n\n[Answered](https://github.com/colinhacks/zod/discussions/4927#discussioncomment-13818620)by [dosubot](https://github.com/apps/dosubot \"dosubot[bot]\") bot[Return to top](https://github.com/colinhacks/zod/discussions/4927#top)\n\nDiscussion options\n\n### Uh oh!\n\nThere was an error while loading. [Please reload this page](https://github.com/colinhacks/zod/discussions/4927).\n\n# {{title}}\n\nQuote reply\n\n## [\\ flekschas](https://github.com/flekschas) [on Jul 19, 2025Jul 19, 2025](https://github.com/colinhacks/zod/discussions/4927\\#discussion-8604193)\n\nOriginal comment in English -\nTranslate to English\n\n| |\n| --- |\n| I'm excited about the newly added `toJSONSchema()` function to turn a Zod schema into a JSON schema. One critical feature I'm wondering about is whether it's possible to add descriptions to the Zod schema such that they show up in the converted JSON schema.<br>For example, I'd love to do the following<br>```<br>import { z } from \"zod\";<br> <br>const Person = z.object({<br> name: z.string().describe('The name of the person'),<br> age: z.number().describe('The age in milliseconds since year 1337'),<br>});<br> <br>z.toJSONSchema(schema)<br>// => {<br>// type: 'object',<br>// properties: {<br>// name: { type: 'string', description: 'The name of the person' },<br>// age: { type: 'number', description: 'The age in milliseconds since year 1337' }<br>// },<br>// required: [ 'name', 'age' ],<br>// additionalProperties: false,<br>// }<br>```<br>Is this at all possible right now? |\n\nBetaWas this translation helpful? [Give feedback.](https://github.com/colinhacks/zod/discussions/4927#)\n\n3You must be logged in to vote\n\nAll reactions\n\nAnswered by\n[dosubot\\[bot\\]](https://github.com/dosubot[bot])[on Jul 19, 2025Jul 19, 2025](https://github.com/colinhacks/zod/discussions/4927#discussioncomment-13818620)\n\nHi [@flekschas](https://github.com/flekschas)! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the zod team.\n\nYes, this is fully supported in Zod v4. Descriptions you add with `.describe()` (or with `.meta({ description: ... })`) are included in the output of `toJSONSchema()`. Your example will work as expected, and the descriptions will appear in the generated JSON Schema under each property.\n\nFor more complex metadata (like `title`, `examples`, etc.), use `.meta()`, but for simple descriptions, `.describe()` is fine. Here’s an example from the docs:\n\n```\nconst mySchema = z.object({\n firstName: z.string().describe(\"Your first name\"),\n lastName: z.string().meta({ title: \"last_name\" }),\n age: z.number().meta({ examples: [12, 99] }),\n});\n\nz.toJSONSchema(mySchema);\n```\n\n[View full answer](https://github.com/colinhacks/zod/discussions/4927#discussioncomment-13818620)\n\n## Replies: 1 comment · 1 reply\n\nComment options\n\n### Uh oh!\n\nThere was an error while loading. [Please reload this page](https://github.com/colinhacks/zod/discussions/4927).\n\n# {{title}}\n\nQuote reply\n\n### [\\ dosubot\\[bot\\]](https://github.com/apps/dosubot) bot [on Jul 19, 2025Jul 19, 2025](https://github.com/colinhacks/zod/discussions/4927\\#discussioncomment-13818620)\n\nOriginal comment in English -\nTranslate to English\n\n| |\n| --- |\n| Hi [@flekschas](https://github.com/flekschas)! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the zod team.<br>Yes, this is fully supported in Zod v4. Descriptions you add with `.describe()` (or with `.meta({ description: ... })`) are included in the output of `toJSONSchema()`. Your example will work as expected, and the descriptions will appear in the generated JSON Schema under each property.<br>For more complex metadata (like `title`, `examples`, etc.), use `.meta()`, but for simple descriptions, `.describe()` is fine. Here’s an example from the docs:<br>```<br>const mySchema = z.object({<br> firstName: z.string().describe(\"Your first name\"),<br> lastName: z.string().meta({ title: \"last_name\" }),<br> age: z.number().meta({ examples: [12, 99] }),<br>});<br>z.toJSONSchema(mySchema);<br>// => {<br>// type: 'object',<br>// properties: {<br>// firstName: { type: 'string', description: 'Your first name' },<br>// lastName: { type: 'string', title: 'last_name' },<br>// age: { type: 'number', examples: [12, 99] }<br>// },<br>// required: [ 'firstName', 'lastName', 'age' ]<br>// }<br>```<br>You can find more details in the [metadata docs](https://zod.dev/metadata) and the [JSON Schema conversion section](https://zod.dev/json-schema). If this answers your question, feel free to close the issue!<br>_To reply, just mention [@dosu](https://go.dosu.dev/dosubot)._<br>* * *<br>How did I do? [Good](https://app.dosu.dev/response-feedback/1f1cd265-62e5-4d31-8a8a-bbd5cd1f701d?feedback_type=great_response) \\| [Irrelevant](https://app.dosu.dev/response-feedback/1f1cd265-62e5-4d31-8a8a-bbd5cd1f701d?feedback_type=irrelevant_answer) \\| [Incorrect](https://app.dosu.dev/response-feedback/1f1cd265-62e5-4d31-8a8a-bbd5cd1f701d?feedback_type=incorrect_sources) \\| [Verbose](https://app.dosu.dev/response-feedback/1f1cd265-62e5-4d31-8a8a-bbd5cd1f701d?feedback_type=too_verbose) \\| [Hallucination](https://app.dosu.dev/response-feedback/1f1cd265-62e5-4d31-8a8a-bbd5cd1f701d?feedback_type=hallucination) \\| [Report 🐛](https://app.dosu.dev/response-feedback/1f1cd265-62e5-4d31-8a8a-bbd5cd1f701d?feedback_type=bug_report) \\| [Other](https://app.dosu.dev/response-feedback/1f1cd265-62e5-4d31-8a8a-bbd5cd1f701d?feedback_type=other) [](https://go.dosu.dev/discord-bot) [](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/colinhacks/zod/discussions/4927) |\n\nBetaWas this translation helpful? [Give feedback.](https://github.com/colinhacks/zod/discussions/4927#)\n\nMarked as answer\n\n0You must be logged in to vote\n\n1\n\nAll reactions\n\n- 1\n\n1 reply\n\n\n[](https://github.com/flekschas)\n\nComment options\n\n### Uh oh!\n\nThere was an error while loading. [Please reload this page](https://github.com/colinhacks/zod/discussions/4927).\n\n# {{title}}\n\nQuote reply\n\n#### [flekschas](https://github.com/flekschas) [on Jul 19, 2025Jul 19, 2025](https://github.com/colinhacks/zod/discussions/4927\\#discussioncomment-13818632) Author\n\nOriginal comment in English -\nTranslate to English\n\n| |\n| --- |\n| Oh boy, I just found it too 🤦♂️ Sorry for the noise. |\n\nBetaWas this translation helpful? [Give feedback.](https://github.com/colinhacks/zod/discussions/4927#)\n\n1\n\nAll reactions\n\n- 1\n\nAnswer selected by [flekschas](https://github.com/flekschas)\n\n[Sign up for free](https://github.com/join?source=comment-repo) **to join this conversation on GitHub**.\nAlready have an account?\n[Sign in to comment](https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fcolinhacks%2Fzod%2Fdiscussions%2F4927)\n\nCategory\n\n\n[\\\\\n\\\\\nQ&A](https://github.com/colinhacks/zod/discussions/categories/q-a)\n\nLabels\n\n\nNone yet\n\n\n1 participant\n\n\n[](https://github.com/flekschas)\n\nHeading\n\nBold\n\nItalic\n\nQuote\n\nCode\n\nLink\n\n* * *\n\nNumbered list\n\nUnordered list\n\nTask list\n\n* * *\n\nAttach files\n\nMention\n\nReference\n\n# Select a reply\n\nLoading\n\n[Create a new saved reply](https://github.com/colinhacks/zod/discussions/4927)\n\n👍1 reacted with thumbs up emoji👎1 reacted with thumbs down emoji😄1 reacted with laugh emoji🎉1 reacted with hooray emoji😕1 reacted with confused emoji❤️1 reacted with heart emoji🚀1 reacted with rocket emoji👀1 reacted with eyes emoji\n\nYou can’t perform that action at this time.","metadata":{"octolytics-url":"https://collector.github.com/github/collect","fb:app_id":"1401488693436528","google-site-verification":"Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I","github-keyboard-shortcuts":"repository,copilot","og:url":"https://github.com/colinhacks/zod/discussions/4927","octolytics-dimension-repository_network_root_id":"245704608","user-login":"","browser-stats-url":"https://api.github.com/_private/browser/stats","hovercard-subject-tag":"discussion:8604193","language":"en","title":"Add descriptions to schemas for conversion to JSON Schema · colinhacks/zod · Discussion #4927 · GitHub","turbo-body-classes":"logged-out env-production page-responsive","route-action":"discussion_layout","og:description":"I'm excited about the newly added toJSONSchema() function to turn a Zod schema into a JSON schema. One critical feature I'm wondering about is whether it's possible to add descriptions to the Zod s...","og:image:width":"1200","hostname":"github.com","octolytics-dimension-repository_network_root_nwo":"colinhacks/zod","octolytics-dimension-repository_nwo":"colinhacks/zod","ui-target":"canary-2","og:image":"https://opengraph.githubassets.com/356f6cbfafe6ce0e4d5e6e93737c1bc44041a910150e2f23999201c9133ae428/colinhacks/zod/discussions/4927","current-catalog-service-hash":"9f0abe34da433c9b6db74bffa2466494a717b579a96b30a5d252e5090baea7be","visitor-hmac":"862bbe5990d988964fec7093ebc34303d654a5d499f9429589c212192ba59f82","twitter:description":"I'm excited about the newly added toJSONSchema() function to turn a Zod schema into a JSON schema. One critical feature I'm wondering about is whether it's possible to add descriptions ...","turbo-cache-control":"no-preview","og:site_name":"GitHub","ogUrl":"https://github.com/colinhacks/zod/discussions/4927","description":"Add descriptions to schemas for conversion to JSON Schema","octolytics-dimension-user_login":"colinhacks","apple-itunes-app":"app-id=1477376905, app-argument=https://github.com/_view_fragments/Voltron::DiscussionsFragmentsController/show/colinhacks/zod/4927/discussion_layout","color-scheme":"light dark","disable-turbo":"false","octolytics-dimension-repository_is_fork":"false","fetch-nonce":"v2:c8c1cf2a-c976-4fbc-30c4-a93bbb9aeaa4","expected-hostname":"github.com","ogDescription":"I'm excited about the newly added toJSONSchema() function to turn a Zod schema into a JSON schema. One critical feature I'm wondering about is whether it's possible to add descriptions to the Zod s...","ogImage":"https://opengraph.githubassets.com/356f6cbfafe6ce0e4d5e6e93737c1bc44041a910150e2f23999201c9133ae428/colinhacks/zod/discussions/4927","request-id":"9231:10510:4282A54:551583B:69C90C4B","octolytics-dimension-repository_public":"true","release":"51d2e33e3d1e4839c3ced5f8e35c7a47d3a60f32","og:type":"object","octolytics-dimension-user_id":"3084745","visitor-payload":"eyJyZWZlcnJlciI6Imh0dHBzOi8vd3d3Lmdvb2dsZS5jb20vIiwicmVxdWVzdF9pZCI6IjkyMzE6MTA1MTA6NDI4MkE1NDo1NTE1ODNCOjY5QzkwQzRCIiwidmlzaXRvcl9pZCI6IjQ4NjQ1MDM2NjUxNDg4OTAxODciLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ==","html-safe-nonce":"24c60fd6656333c4c63d9b8effa2581466b400a333e04d4b476081370d837159","og:image:alt":"I'm excited about the newly added toJSONSchema() function to turn a Zod schema into a JSON schema. One critical feature I'm wondering about is whether it's possible to add descriptions to the Zod s...","twitter:title":"Add descriptions to schemas for conversion to JSON Schema · colinhacks/zod · Discussion #4927","analytics-location":"/<user-name>/<repo-name>/voltron/discussions_fragments/discussion_layout","twitter:image":"https://opengraph.githubassets.com/356f6cbfafe6ce0e4d5e6e93737c1bc44041a910150e2f23999201c9133ae428/colinhacks/zod/discussions/4927","og:image:height":"600","route-controller":"voltron_discussions_fragments","theme-color":"#1e2327","ogSiteName":"GitHub","route-pattern":"/_view_fragments/Voltron::DiscussionsFragmentsController/show/:user_id/:repository/:discussion_number/discussion_layout(.:format)","og:title":"Add descriptions to schemas for conversion to JSON Schema · colinhacks/zod · Discussion #4927","go-import":"github.com/colinhacks/zod git https://github.com/colinhacks/zod.git","twitter:card":"summary_large_image","octolytics-dimension-repository_id":"245704608","viewport":"width=device-width","ogTitle":"Add descriptions to schemas for conversion to JSON Schema · colinhacks/zod · Discussion #4927","browser-errors-url":"https://api.github.com/_private/browser/errors","twitter:site":"@github","favicon":"https://github.githubassets.com/favicons/favicon.svg","scrapeId":"019d3958-051d-7417-9498-72c5766cdc3e","sourceURL":"https://github.com/colinhacks/zod/discussions/4927","url":"https://github.com/colinhacks/zod/discussions/4927","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","cacheState":"miss","indexId":"477e688c-f663-4dee-9165-41c939385d07","creditsUsed":1}},{"url":"https://zod.dev/metadata","title":"Metadata and registries - Zod","description":"JSON Schema. How to convert Zod schemas to JSON Schema. On this page. Registries .register(). Metadata. z.globalRegistry .meta() .describe(). Custom registries.","position":3,"markdown":"💎 Zod 4 is now stable! [Read the announcement.](https://zod.dev/v4)\n\nAsk AI\n\n\n# Metadata and registries\n\nCopy markdown\n[Edit this page](https://github.com/colinhacks/zod/edit/main/packages/docs/content/metadata.mdx)\n\nIt's often useful to associate a schema with some additional _metadata_ for documentation, code generation, AI structured outputs, form validation, and other purposes.\n\n## [Registries](https://zod.dev/metadata?id=registries)\n\nMetadata in Zod is handled via _registries_. Registries are collections of schemas, each associated with some _strongly-typed_ metadata. To create a simple registry:\n\n```\nimport * as z from \"zod\";\n\nconst myRegistry = z.registry<{ description: string }>();\n```\n\nTo register, lookup, and remove schemas from this registry:\n\n```\nconst mySchema = z.string();\n\nmyRegistry.add(mySchema, { description: \"A cool schema!\"});\nmyRegistry.has(mySchema); // => true\nmyRegistry.get(mySchema); // => { description: \"A cool schema!\" }\nmyRegistry.remove(mySchema);\nmyRegistry.clear(); // wipe registry\n```\n\nTypeScript enforces that the metadata for each schema matches the registry's **metadata type**.\n\n```\nmyRegistry.add(mySchema, { description: \"A cool schema!\" }); // ✅\nmyRegistry.add(mySchema, { description: 123 }); // ❌\n```\n\n**Special handling for `id`** — Zod registries treat the `id` property specially. An `Error` will be thrown if multiple schemas are registered with the same `id` value. This is true for all registries, including the global registry.\n\n### [`.register()`](https://zod.dev/metadata?id=register)\n\n**Note** — This method is special in that it does not return a new schema; instead, it returns the original schema. No other Zod method does this! That includes `.meta()` and `.describe()` (documented below) which return a new instance.\n\nSchemas provide a `.register()` method to more conveniently add it to a registry.\n\n```\nconst mySchema = z.string();\n\nmySchema.register(myRegistry, { description: \"A cool schema!\" });\n// => mySchema\n```\n\nThis lets you define metadata \"inline\" in your schemas.\n\n```\nconst mySchema = z.object({\n name: z.string().register(myRegistry, { description: \"The user's name\" }),\n age: z.number().register(myRegistry, { description: \"The user's age\" }),\n})\n```\n\nIf a registry is defined without a metadata type, you can use it as a generic \"collection\", no metadata required.\n\n```\nconst myRegistry = z.registry();\n\nmyRegistry.add(z.string());\nmyRegistry.add(z.number());\n```\n\n## [Metadata](https://zod.dev/metadata?id=metadata)\n\n### [`z.globalRegistry`](https://zod.dev/metadata?id=zglobalregistry)\n\nFor convenience, Zod provides a global registry (`z.globalRegistry`) that can be used to store metadata for JSON Schema generation or other purposes. It accepts the following metadata:\n\n```\nexport interface GlobalMeta {\n id?: string ;\n title?: string ;\n description?: string;\n deprecated?: boolean;\n [k: string]: unknown;\n}\n```\n\nTo register some metadata in `z.globalRegistry` for a schema:\n\n```\nimport * as z from \"zod\";\n\nconst emailSchema = z.email().register(z.globalRegistry, {\n id: \"email_address\",\n title: \"Email address\",\n description: \"Your email address\",\n examples: [\"first.last@example.com\"]\n});\n```\n\nTo globally augment the `GlobalMeta` interface, use [_declaration merging_](https://www.typescriptlang.org/docs/handbook/declaration-merging.html). Add the following anywhere in your codebase. Creating a `zod.d.ts` file in your project root is a common convention.\n\n```\ndeclare module \"zod\" {\n interface GlobalMeta {\n // add new fields here\n examples?: unknown[];\n }\n}\n\n// forces TypeScript to consider the file a module\nexport {}\n```\n\n### [`.meta()`](https://zod.dev/metadata?id=meta)\n\nFor a more convenient approach, use the `.meta()` method to register a schema in `z.globalRegistry`.\n\nZodZod Mini\n\n```\nconst emailSchema = z.email().meta({\n id: \"email_address\",\n title: \"Email address\",\n description: \"Please enter a valid email address\",\n});\n```\n\nCalling `.meta()` without an argument will _retrieve_ the metadata for a schema.\n\n```\nemailSchema.meta();\n// => { id: \"email_address\", title: \"Email address\", ... }\n```\n\nMetadata is associated with a _specific schema instance._ This is important to keep in mind, especially since Zod methods are immutable—they always return a new instance.\n\n```\nconst A = z.string().meta({ description: \"A cool string\" });\nA.meta(); // => { description: \"A cool string\" }\n\nconst B = A.refine(_ => true);\nB.meta(); // => undefined\n```\n\n### [`.describe()`](https://zod.dev/metadata?id=describe)\n\nThe `.describe()` method still exists for compatibility with Zod 3, but `.meta()` is now the recommended approach.\n\nThe `.describe()` method is a shorthand for registering a schema in `z.globalRegistry` with just a `description` field.\n\nZodZod Mini\n\n```\nconst emailSchema = z.email();\nemailSchema.describe(\"An email address\");\n\n// equivalent to\nemailSchema.meta({ description: \"An email address\" });\n```\n\n## [Custom registries](https://zod.dev/metadata?id=custom-registries)\n\nYou've already seen a simple example of a custom registry:\n\n```\nimport * as z from \"zod\";\n\nconst myRegistry = z.registry<{ description: string };>();\n```\n\nLet's look at some more advanced patterns.\n\n### [Referencing inferred types](https://zod.dev/metadata?id=referencing-inferred-types)\n\nIt's often valuable for the metadata type to reference the _inferred type_ of a schema. For instance, you may want an `examples` field to contain examples of the schema's output.\n\n```\nimport * as z from \"zod\";\n\ntype MyMeta = { examples: z.$output[] };\nconst myRegistry = z.registry<MyMeta>();\n\nmyRegistry.add(z.string(), { examples: [\"hello\", \"world\"] });\nmyRegistry.add(z.number(), { examples: [1, 2, 3] });\n```\n\nThe special symbol `z.$output` is a reference to the schemas inferred output type (`z.infer<typeof schema>`). Similarly you can use `z.$input` to reference the input type.\n\n### [Constraining schema types](https://zod.dev/metadata?id=constraining-schema-types)\n\nPass a second generic to `z.registry()` to constrain the schema types that can be added to a registry. This registry only accepts string schemas.\n\n```\nimport * as z from \"zod\";\n\nconst myRegistry = z.registry<{ description: string }, z.ZodString>();\n\nmyRegistry.add(z.string(), { description: \"A number\" }); // ✅\nmyRegistry.add(z.number(), { description: \"A number\" }); // ❌\n// ^ 'ZodNumber' is not assignable to parameter of type 'ZodString'\n```\n\n[Formatting errors\\\\\n\\\\\nUtilities for formatting and displaying Zod errors](https://zod.dev/error-formatting) [JSON Schema\\\\\n\\\\\nHow to convert Zod schemas to JSON Schema](https://zod.dev/json-schema)\n\n### On this page\n\n[Registries](https://zod.dev/metadata#registries) [`.register()`](https://zod.dev/metadata#register) [Metadata](https://zod.dev/metadata#metadata) [`z.globalRegistry`](https://zod.dev/metadata#zglobalregistry) [`.meta()`](https://zod.dev/metadata#meta) [`.describe()`](https://zod.dev/metadata#describe) [Custom registries](https://zod.dev/metadata#custom-registries) [Referencing inferred types](https://zod.dev/metadata#referencing-inferred-types) [Constraining schema types](https://zod.dev/metadata#constraining-schema-types)","metadata":{"ogSiteName":"Zod","og:image:height":"630","twitter:image:alt":"Metadata and registries | Zod","og:description":"Attaching and manipulatinvg metadata on Zod schemas","og:image:width":"1200","og:site_name":"Zod","viewport":"width=device-width, initial-scale=1","og:type":"website","twitter:site":"@colinhacks","language":"en","next-size-adjust":"","twitter:description":"Attaching and manipulatinvg metadata on Zod schemas","ogTitle":"Metadata and registries | Zod","twitter:image":"https://zod.dev/og.png?title=Metadata%20and%20registries&description=Attaching%20and%20manipulatinvg%20metadata%20on%20Zod%20schemas&path=zod.dev%2Fmetadata","keywords":"zod,typescript,validation,schema","og:url":"https://zod.dev/metadata","og:title":"Metadata and registries | Zod","twitter:card":"summary_large_image","ogDescription":"Attaching and manipulatinvg metadata on Zod schemas","twitter:creator":"@colinhacks","ogUrl":"https://zod.dev/metadata","ogImage":"https://zod.dev/og.png?title=Metadata%20and%20registries&description=Attaching%20and%20manipulatinvg%20metadata%20on%20Zod%20schemas&path=zod.dev%2Fmetadata","description":"Attaching and manipulatinvg metadata on Zod schemas","og:image":"https://zod.dev/og.png?title=Metadata%20and%20registries&description=Attaching%20and%20manipulatinvg%20metadata%20on%20Zod%20schemas&path=zod.dev%2Fmetadata","title":"Metadata and registries | Zod","og:image:alt":"Metadata and registries | Zod","twitter:image:width":"1200","twitter:title":"Metadata and registries | Zod","twitter:image:height":"630","favicon":"https://zod.dev/icon.png?39fe259ddd7f4224","scrapeId":"019d3958-051d-7417-9498-74ca3f404a05","sourceURL":"https://zod.dev/metadata","url":"https://zod.dev/metadata","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","cacheState":"miss","indexId":"f04fcb56-2368-4e33-8849-d2f9393936b7","creditsUsed":1}}]}} |