Hash zod
Zod v4 TypeScript schema validation patterns and best practices. Use when writing or modifying Zod schemas, adding schema annotations/metadata, or validating data with Zod.
install
source · Clone the upstream repo
git clone https://github.com/hashintel/hash
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/hashintel/hash "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.codex/skills/zod" ~/.claude/skills/hashintel-hash-zod-8d69de && rm -rf "$T"
manifest:
.codex/skills/zod/SKILL.mdsource content
Zod v4 Schema Validation
TypeScript-first schema validation library with static type inference. This skill covers v4 patterns including the metadata registry system.
Schema Annotations
Zod v4 stores metadata in registries (primarily
z.globalRegistry). Use .meta() as the primary API and .describe() as shorthand for description-only cases.
IMPORTANT: Always call
.meta()/.describe() last in the method chain. Methods like .min(), .optional(), .extend() return new schema instances, so metadata must be attached at the end.
// ✅ Correct - .meta() at end of chain z.string().min(1).max(100).meta({ description: "User's full name", label: "Name" }) // ✅ Correct - .describe() shorthand for description only z.string().email().describe("Primary email address") // ❌ Wrong - metadata lost because .optional() creates new instance z.string().meta({ description: "Lost!" }).optional()
Annotating object properties:
const userSchema = z.object({ email: z.string().email().meta({ description: "Used for login", label: "Email Address", placeholder: "you@example.com" }), age: z.number().min(0).describe("User's age in years"), });
API guidance:
- Use
for structured metadata (title, description, examples, UI hints).meta(obj) - Use
as shorthand when only a description string is needed.describe(text) - Read
for backwards compatibility with v3 tooling.description - Never mutate
directly—always use.description
/.meta()
to set.describe()
Gotchas
- Metadata chain order:
/.meta()
MUST be called last—schema methods return new instances.describe() - Don't mutate .description: Read
for v3 compatibility, but always set via.description
or.meta().describe() - .email(), .uuid(), etc. are top-level: Use
notz.email()
(latter deprecated)z.string().email() - z.object() strips unknown keys: Use
to reject unknown keysz.strictObject() - .merge() deprecated: Use
instead.extend()
References
- Complete API reference: https://zod.dev/llms.txt
- Schema types: https://zod.dev/api
- Metadata/registries: https://zod.dev/metadata
- JSON Schema conversion: https://zod.dev/json-schema
- v3 → v4 migration: https://zod.dev/v4/changelog