The metadata and registry system provides mechanisms for attaching strongly-typed metadata to schemas and organizing schemas into collections. Registries are created with z.registry<T>() and schemas can be registered using .register() or convenience methods like .meta() and .describe(). The system serves three primary use cases: JSON Schema generation, documentation/tooling, and AI-powered structured output systems.
The system consists of registry objects ($ZodRegistry<T>) that store schema-to-metadata mappings, schema methods for metadata attachment, and internal symbols ($output, $input) for type replacement.
The following diagram maps the natural language concepts of "Registries" and "Metadata" to the specific classes and symbols used in the Zod codebase.
Sources: packages/zod/src/v4/core/registries.ts4-31 packages/zod/src/v4/core/registries.ts94-105 packages/docs/content/metadata.mdx13-76
Custom registries are created with z.registry<MetadataType>() where MetadataType defines the structure of metadata objects packages/docs/content/metadata.mdx15-21 The built-in z.globalRegistry is a $ZodRegistry<GlobalMeta> instance attached to globalThis to prevent dual-package hazards between CJS and ESM builds packages/zod/src/v4/core/registries.ts94-105
| Method | Purpose | Implementation Detail |
|---|---|---|
add(schema, meta) | Add schema with metadata | Sets entry in _map and updates _idmap if id exists in meta packages/zod/src/v4/core/registries.ts33-43 |
get(schema) | Retrieve metadata | Inherits metadata from schema._zod.parent while stripping the id property to ensure ID uniqueness packages/zod/src/v4/core/registries.ts60-72 |
has(schema) | Check existence | Checks _map.has(schema) packages/zod/src/v4/core/registries.ts74-76 |
remove(schema) | Delete entry | Removes from both _map and _idmap packages/zod/src/v4/core/registries.ts51-58 |
clear() | Wipe registry | Re-initializes WeakMap and Map storage packages/zod/src/v4/core/registries.ts45-49 |
The id property in metadata receives special handling: registries track schemas by ID in _idmap. While add() silently overwrites duplicate IDs within the registry instance packages/zod/src/v4/classic/tests/registries.test.ts210-220 downstream tools like toJSONSchema will throw if they encounter different schemas sharing the same ID during conversion packages/zod/src/v4/classic/tests/registries.test.ts222-230
Sources: packages/zod/src/v4/core/registries.ts27-77 packages/docs/content/metadata.mdx13-43
Zod uses a sophisticated type replacement utility, $replace<Meta, S>, to allow metadata definitions to reference the inferred types of the schema they are attached to.
$output: A unique symbol used in metadata definitions to represent z.output<S> (the inferred output type) packages/zod/src/v4/core/registries.ts4-5$input: A unique symbol used to represent z.input<S> (the raw input type) packages/zod/src/v4/core/registries.ts6-7The $replace type recursively traverses the metadata object. If it encounters $output or $input, it replaces them with the schema's actual inferred types packages/zod/src/v4/core/registries.ts9-24 This allows for type-safe metadata like examples that must match the schema's output packages/docs/content/metadata.mdx211-224
Sources: packages/zod/src/v4/core/registries.ts9-24 packages/zod/src/v4/classic/tests/registries.test.ts93-130
Schemas provide methods to register themselves. Note the critical difference in return types:
| Method | Target Registry | Return Value | Behavior |
|---|---|---|---|
.register(registry, meta) | Any registry | Original schema | Inline registration without creating a new schema instance packages/docs/content/metadata.mdx45-56 |
.meta(metadata) | z.globalRegistry | New schema instance | Standard immutable transformation; creates a clone packages/docs/content/metadata.mdx124-147 |
.describe(string) | z.globalRegistry | New schema instance | Shorthand for .meta({ description }) packages/docs/content/metadata.mdx167-194 |
Zod 4 supports metadata inheritance across schema clones (e.g., when calling .optional()). When calling .get(schema), the registry checks schema._zod.parent. If a parent exists, it merges parent metadata with the current schema's metadata, ensuring properties like title persist while unique properties like id are discarded during the merge packages/zod/src/v4/core/registries.ts64-70
Sources: packages/zod/src/v4/core/registries.ts60-72 packages/zod/src/v4/classic/tests/registries.test.ts156-163
z.globalRegistry)The z.globalRegistry is a singleton instance shared across environments packages/zod/src/v4/core/registries.ts104-105 It uses the GlobalMeta interface, which extends JSONSchemaMeta packages/zod/src/v4/core/registries.ts79-87
The default interface includes common fields used for documentation and JSON Schema generation packages/zod/src/v4/core/registries.ts79-85:
Users can augment GlobalMeta to add custom fields recognized by .meta() packages/docs/content/metadata.mdx108-120:
Sources: packages/zod/src/v4/core/registries.ts79-87 packages/docs/content/metadata.mdx108-120
In zod/mini, metadata is attached using the functional .check() method with z.meta() or z.describe() helper functions packages/docs/content/packages/mini.mdx195-198
This functional approach ensures that metadata registration logic can be tree-shaken if the specific metadata functions are not imported or used in the final bundle packages/docs/content/packages/mini.mdx35-47
Sources: packages/docs/content/packages/mini.mdx195-198 packages/docs/content/metadata.mdx136-147
Refresh this wiki
This wiki was recently refreshed. Please wait 1 day to refresh again.