Structured Output Guide
Get predictable, typed responses from Claude using Zod schemas with automatic validation and retry.Basic Schema
Copy
Ask AI
import { z } from "zod";
const ResultSchema = z.object({
success: z.boolean(),
message: z.string(),
});
<Claude
schema={ResultSchema}
onFinished={(result) => {
// result.structured is typed!
console.log(result.structured.success);
console.log(result.structured.message);
}}
>
Complete the task.
</Claude>
Schema with Descriptions
Descriptions help Claude understand what each field should contain:Copy
Ask AI
const AnalysisSchema = z.object({
summary: z.string().describe("A 1-2 sentence summary of the findings"),
confidence: z.number().min(0).max(1).describe("Confidence level from 0 to 1"),
issues: z.array(z.object({
severity: z.enum(["low", "medium", "high", "critical"])
.describe("Impact level of this issue"),
description: z.string().describe("Clear description of the issue"),
location: z.string().describe("File path and line number"),
})).describe("List of issues found, ordered by severity"),
});
Enums and Literals
Copy
Ask AI
const StatusSchema = z.object({
status: z.enum(["success", "failure", "pending"]),
errorCode: z.literal("E001").optional(),
category: z.union([
z.literal("bug"),
z.literal("feature"),
z.literal("refactor"),
]),
});
Arrays and Objects
Copy
Ask AI
const ReportSchema = z.object({
// Simple array
tags: z.array(z.string()),
// Array of objects
findings: z.array(z.object({
type: z.string(),
message: z.string(),
})),
// Nested objects
metadata: z.object({
author: z.string(),
timestamp: z.string(),
version: z.number(),
}),
// Record type
scores: z.record(z.string(), z.number()),
});
Optional and Default Values
Copy
Ask AI
const ConfigSchema = z.object({
name: z.string(),
description: z.string().optional(),
enabled: z.boolean().default(true),
maxRetries: z.number().default(3),
});
Validation and Retry
Smithers automatically retries if validation fails:Copy
Ask AI
<Claude
schema={AnalysisSchema}
maxRetries={2} // Retry up to 2 times on validation failure
retryOnValidationFailure // Default: true
onFinished={(result) => {
// Guaranteed to match schema
}}
onError={(err) => {
// Called after all retries exhausted
console.error("Validation failed:", err);
}}
>
Analyze the code.
</Claude>
Custom Validation
Add business logic validation:Copy
Ask AI
<Claude
schema={AnalysisSchema}
validate={(result) => {
// Must have at least one finding
if (result.issues.length === 0) {
return false;
}
// Score must be reasonable
if (result.score < 0 || result.score > 100) {
return false;
}
return true;
}}
maxRetries={2}
>
Find issues in the code.
</Claude>
TypeScript Integration
Full type inference:Copy
Ask AI
const UserSchema = z.object({
name: z.string(),
email: z.string().email(),
role: z.enum(["admin", "user", "guest"]),
});
// Infer the type
type User = z.infer<typeof UserSchema>;
<Claude<User>
schema={UserSchema}
onFinished={(result) => {
const user: User = result.structured!;
// Fully typed access
console.log(user.name);
console.log(user.email);
console.log(user.role); // "admin" | "user" | "guest"
}}
>
Complex Real-World Example
Copy
Ask AI
const CodeReviewSchema = z.object({
summary: z.string().describe("Executive summary of the review"),
verdict: z.enum(["approve", "request_changes", "comment"])
.describe("Overall review verdict"),
issues: z.array(z.object({
severity: z.enum(["critical", "major", "minor", "nitpick"]),
category: z.enum(["security", "performance", "maintainability", "correctness", "style"]),
file: z.string(),
line: z.number().optional(),
title: z.string().max(100),
description: z.string(),
suggestion: z.string().optional(),
autoFixable: z.boolean().default(false),
})).describe("Issues found during review"),
positives: z.array(z.string())
.describe("Good practices observed"),
metrics: z.object({
linesReviewed: z.number(),
filesReviewed: z.number(),
complexityScore: z.number().min(1).max(10),
}),
nextSteps: z.array(z.string())
.describe("Recommended actions"),
});
<Claude
model="opus"
schema={CodeReviewSchema}
maxRetries={3}
allowedTools={["Read", "Glob", "Grep"]}
onFinished={(result) => {
const review = result.structured!;
if (review.verdict === "approve") {
console.log("✅ Review approved!");
} else {
console.log(`❌ ${review.issues.length} issues found`);
for (const issue of review.issues) {
console.log(` [${issue.severity}] ${issue.title}`);
}
}
}}
>
Review the changes in this PR comprehensively.
</Claude>
Prompt Tips
Help Claude produce valid output:Copy
Ask AI
<Claude schema={Schema}>
Analyze the code and return a structured response.
Important:
- All required fields must be present
- Severity must be one of: low, medium, high, critical
- Score must be between 0 and 100
- Include at least one recommendation
</Claude>
Handling Errors
Copy
Ask AI
<Claude
schema={Schema}
onFinished={(result) => {
if (!result.structured) {
console.error("No structured output despite validation passing");
return;
}
// Use result.structured safely
}}
onError={(error) => {
if (error.message.includes("validation")) {
console.error("Schema validation failed after retries");
} else {
console.error("Execution error:", error);
}
}}
>