This page provides guidance for developers building libraries that integrate with Zod. It covers dependency configuration, import paths, version support strategies, and best practices for accepting and parsing user-defined schemas.
For information about the differences between Zod 4 Classic and Mini APIs, see Classic API and Mini API. For details about the shared core package, see Core Package.
Before depending on Zod directly, evaluate whether Standard Schema meets your needs. Standard Schema is a shared interface implemented by most TypeScript validation libraries, including Zod, Valibot, ArkType, and others.
If your library accepts user-defined schemas as "black box" validators—extracting inferred types, validating inputs, and handling standardized errors—Standard Schema eliminates the need for library-specific dependencies. This approach maximizes compatibility across the validation ecosystem.
Depend on Zod directly only when you require:
_zod.def properties)Sources: packages/docs/content/library-authors.mdx29-38
Libraries built on Zod should declare "zod" as a peer dependency, allowing users to provide their own Zod installation. This prevents version conflicts and ensures a single Zod instance across the dependency tree.
The minimum version ^3.25.0 is required because the "zod/v4/core" subpath was introduced in that release. During development, include "zod" in devDependencies to satisfy the peer dependency requirement.
Sources: packages/docs/content/library-authors.mdx40-66
Import from permalink subpaths that remain stable across versions:
| Subpath | Purpose | Status |
|---|---|---|
"zod/v3" | Zod 3 API | ✅ Permanent |
"zod/v4/core" | Zod 4 shared core | ✅ Permanent |
"zod" | Main export (Zod 3 in 3.x, Zod 4 in 4.x) | ❌ Avoid |
"zod/v4" | Zod 4 Classic API | ❌ Avoid |
"zod/v4/mini" | Zod 4 Mini API | ❌ Avoid |
The "zod" main export changes meaning across major versions. The "zod/v4" and "zod/v4/mini" subpaths export API-specific classes that are incompatible with each other. Building against "zod/v4/core" ensures your library works with both Classic and Mini.
Sources: packages/docs/content/library-authors.mdx82-97 packages/zod/package.json36-114
Starting in [email protected], both Zod 3 and Zod 4 coexist at their respective subpaths. Import both to accept schemas from either version:
The presence of the _zod property distinguishes Zod 4 schemas from Zod 3. In Zod 3, schema internals are accessed via _def. In Zod 4, they are accessed via _zod.def.
Sources: packages/docs/content/library-authors.mdx106-133
The core package "zod/v4/core" defines base classes that both Classic and Mini extend. By constraining generic parameters to these base classes, your library automatically supports both APIs:
The $-prefixed classes ($ZodType, $ZodObject, $ZodString, etc.) in "zod/v4/core" are the shared base layer. Classic and Mini extend these with their respective method sets.
| Class | Classic Subclass | Mini Subclass | Shared Base |
|---|---|---|---|
| Object | ZodObject | ZodMiniObject | $ZodObject |
| String | ZodString | ZodMiniString | $ZodString |
| Number | ZodNumber | ZodMiniNumber | $ZodNumber |
| Array | ZodArray | ZodMiniArray | $ZodArray |
Sources: packages/docs/content/library-authors.mdx135-166
When accepting user-defined schemas, use the constraint pattern to preserve type information:
The first pattern erases the schema's specific subclass, preventing access to type-specific methods. The second pattern preserves the full type.
Constraining to specific schema types:
Sources: packages/docs/content/library-authors.mdx194-255
The $ZodType base class has no methods. Use top-level parsing functions from "zod/v4/core":
Available functions: parse, safeParse, parseAsync, safeParseAsync, decode, encode, safeDecode, safeEncode, etc.
Sources: packages/docs/content/library-authors.mdx256-265
The $ZodRegistry class attaches metadata to schemas without modifying them. The globalRegistry is a shared instance accessible to all library code.
Type-safe metadata with type replacement:
The $output and $input symbols enable schema-aware metadata:
The $replace utility recursively replaces $output and $input symbols with the actual inferred types.
Sources: packages/zod/src/v4/core/registries.ts1-106 packages/zod/src/v4/classic/tests/registries.test.ts1-244
Metadata is inherited from parent schemas, except for the id property:
When a schema is cloned (e.g., via .optional(), .nullable(), etc.), the new schema inherits its parent's metadata. The id field is explicitly excluded from inheritance to prevent duplicate schema IDs.
Sources: packages/zod/src/v4/classic/tests/registries.test.ts156-163
The version object in "zod/v4/core" provides runtime version information:
This enables version-specific logic when necessary, though well-designed libraries should avoid version-specific code paths.
Sources: packages/zod/src/v4/core/versions.ts1-6
| Task | Recommendation |
|---|---|
| Dependency | Add "zod": "^3.25.0 || ^4.0.0" to peerDependencies |
| Import Path | Use "zod/v4/core" for library code |
| Generic Constraints | Use T extends z4.$ZodType, not z4.$ZodType<T> |
| Parsing | Use z4.parse(), z4.safeParse(), etc. (top-level functions) |
| Introspection | Access schema._zod.def for Zod 4, schema._def for Zod 3 |
| Version Detection | Check "_zod" in schema to distinguish Zod 3 vs 4 |
| Metadata | Use globalRegistry or create custom $ZodRegistry instances |
| Breaking Changes | Adding Zod 4 support does not require a major version bump |
Refresh this wiki