Zod Reference
Zod is a TypeScript-first schema validation library. Use it for runtime validation, parsing data, and inferring TypeScript types from schemas.
Import
// Define schema
const taskSchema = z.object({
title: z.string().min(1, { error: "Title is required" }).max(100, { error: "Title too long" }),
priority: z.enum(["low", "medium", "high"]),
dueDate: z.string().optional(),
});
type Task = z.infer<typeof taskSchema>;
export default function App() {
const [title, setTitle] = React.useState("");
const [priority, setPriority] = React.useState<"low" | "medium" | "high">("medium");
const [errors, setErrors] = React.useState<Record<string, string>>({});
const [tasks, setTasks] = React.useState<Task[]>([]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const result = taskSchema.safeParse({ title, priority });
if (!result.success) {
// Convert Zod errors to a simple object
const fieldErrors: Record<string, string> = {};
result.error.issues.forEach(issue => {
const field = issue.path[0] as string;
fieldErrors[field] = issue.message;
});
setErrors(fieldErrors);
return;
}
// Valid data
setTasks(prev => [...prev, result.data]);
setTitle("");
setErrors({});
};
return (
<div>
<div>
<div>
<h3>Add task</h3>
</div>
<div>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<label htmlFor="title">Title</label>
<input
id="title"
value={title}
onChange={e => setTitle(e.target.value)}
/>
{errors.title && (
<p className="text-sm text-destructive">{errors.title}</p>
)}
</div>
<button type="submit">Add</button>
</form>
</div>
</div>
{tasks.map((task, i) => (
<div key={i}>
<div className="pt-4">
<p>{task.title}</p>
<p className="text-sm text-muted-foreground">{task.priority}</p>
</div>
</div>
))}
</div>
);
}
Use with AI Object Generation
Zod schemas work with the useAIObject hook for structured AI responses:
import { z } from "zod";
import { useAIObject } from "@/hooks/use-ai-object";
const recipeSchema = z.object({
name: z.string(),
ingredients: z.array(z.object({
item: z.string(),
amount: z.string(),
})),
steps: z.array(z.string()),
});
const { object, generate, isLoading } = useAIObject({
schema: recipeSchema,
});
Best Practices
- Define schemas at module level - Reuse schemas across components
- Use
z.infer- Derive types from schemas to keep them in sync - Use
safeParsein forms - Handle validation errors gracefully - Use the
errorparameter - Provide user-friendly error messages - Use top-level formats - Prefer
z.email()overz.string().email()for tree-shaking - Use discriminated unions - For type-safe handling of different object shapes
- Use
.meta()for documentation - Attach metadata for JSON Schema generation