zod
Zod 4+ validation patterns. Use when validating forms, API responses, env vars, or inferring types from schemas.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
Zod 4+
String formats — top-level
// v4: top-level functions
const email = z.email()
const uuid = z.uuid()
const url = z.url()
const iso = z.iso.datetime()
// NOT z.string().email() — that's v3
Record — two arguments required
// v4: key + value schemas
z.record(z.string(), z.number())
// NOT z.record(z.number()) — that's v3
Objects
// Strict (no extra keys)
z.strictObject({ name: z.string() })
// Loose (allow extra keys)
z.looseObject({ name: z.string() })
// .strict() and .passthrough() still work but prefer above
Error customization
// v4: unified 'error' param
z.string({ error: "Must be a string" })
z.email({ error: (issue) => `Invalid: ${issue.input}` })
// NOT 'message', 'invalid_type_error', etc. — that's v3
Metadata
// v4: .meta() with object
z.string().meta({ label: "Username", description: "Your handle" })
// NOT .describe() — that's v3
Unions and intersections
// v4: prefer explicit helpers
z.union([z.string(), z.number()])
z.intersection(baseSchema, extendSchema)
// NOT .or() / .and() chains — those are v3 style
Accessing errors
const result = schema.safeParse(data)
if (!result.success) {
console.log(result.error.issues) // v4: .issues
// NOT .errors — that's v3
}
Form validation with React 19
const formSchema = z.object({
email: z.email({ error: "Invalid email" }),
age: z.coerce.number().min(18, { error: "Must be 18+" })
})
async function action(prev: State, formData: FormData) {
const result = formSchema.safeParse(Object.fromEntries(formData))
if (!result.success) {
return { errors: result.error.issues }
}
// result.data is typed
}
Type inference
const userSchema = z.object({
id: z.uuid(),
email: z.email(),
role: z.enum(["admin", "user"])
})
type User = z.infer<typeof userSchema>
Avoid
-
z.string().email()→z.email() -
z.string().uuid()→z.uuid() -
z.record(value)→z.record(key, value) -
messageparam →errorparam -
.describe()→.meta() -
.or()/.and()→z.union()/z.intersection() -
.errors→.issues
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-03-03
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!