{"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![Chat avatar](https://zod.dev/logo/logo.png)\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>;\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[![@flekschas](https://avatars.githubusercontent.com/u/932103?s=40&v=4)\\\\\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## [![](https://avatars.githubusercontent.com/u/932103?s=64&v=4)\\ 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.
For example, I'd love to do the following
```
import { z } from \"zod\";

const Person = z.object({
name: z.string().describe('The name of the person'),
age: z.number().describe('The age in milliseconds since year 1337'),
});

z.toJSONSchema(schema)
// => {
// type: 'object',
// properties: {
// name: { type: 'string', description: 'The name of the person' },
// age: { type: 'number', description: 'The age in milliseconds since year 1337' }
// },
// required: [ 'name', 'age' ],
// additionalProperties: false,
// }
```
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### [![](https://avatars.githubusercontent.com/in/324583?s=64&v=4)\\ 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.
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.
For more complex metadata (like `title`, `examples`, etc.), use `.meta()`, but for simple descriptions, `.describe()` is fine. Here’s an example from the docs:
```
const mySchema = z.object({
firstName: z.string().describe(\"Your first name\"),
lastName: z.string().meta({ title: \"last_name\" }),
age: z.number().meta({ examples: [12, 99] }),
});
z.toJSONSchema(mySchema);
// => {
// type: 'object',
// properties: {
// firstName: { type: 'string', description: 'Your first name' },
// lastName: { type: 'string', title: 'last_name' },
// age: { type: 'number', examples: [12, 99] }
// },
// required: [ 'firstName', 'lastName', 'age' ]
// }
```
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!
_To reply, just mention [@dosu](https://go.dosu.dev/dosubot)._
* * *
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) [![Join Discord](https://camo.githubusercontent.com/7d2066700925db1e370d956834f9ea0e5bc92cecbe84963fbd2a363e45091b4e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6a6f696e2d3538363546323f6c6f676f3d646973636f7264266c6f676f436f6c6f723d7768697465266c6162656c3d)](https://go.dosu.dev/discord-bot) [![Share on X](https://camo.githubusercontent.com/a8bd7f3fcf3f5e3bd124eee9ae50ae2ac17b40971726665ac2121d48f9aee155/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f582d73686172652d626c61636b)](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\n![tada](https://github.githubassets.com/assets/1f389-36899a2cb781.png)1\n\nAll reactions\n\n- ![tada](https://github.githubassets.com/assets/1f389-36899a2cb781.png)1\n\n1 reply\n\n\n[![@flekschas](https://avatars.githubusercontent.com/u/932103?s=60&v=4)](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\n![+1](https://github.githubassets.com/assets/1f44d-41cb66fe1e22.png)1\n\nAll reactions\n\n- ![+1](https://github.githubassets.com/assets/1f44d-41cb66fe1e22.png)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[![pray](https://github.githubassets.com/assets/1f64f-25af8353fbea.png)\\\\\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[![@flekschas](https://avatars.githubusercontent.com/u/932103?s=48&v=4)](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":"///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![Chat avatar](https://zod.dev/logo/logo.png)\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();\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`). 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}}]}}