Notre avis
Zod v4 est une bibliothèque de validation de schéma orientée TypeScript qui offre une inférence de type statique et une validation au moment de l'exécution avec des performances accrues.
Points forts
- Analyse extrêmement rapide (14 fois plus rapide que la v3)
- Taille de bundle réduite de 66 %
- Inférence de type riche et validateurs intégrés complets (email, UUID, URL, adresses IP, etc.)
Limites
- Nécessite un environnement TypeScript pour tirer pleinement parti des fonctionnalités
- Courbe d'apprentissage pour les motifs avancés comme les unions discriminées et les templates littéraux
- Peut être excessif pour des validations simples
Utilisez Zod v4 lorsque vous avez besoin d'une validation robuste au moment de l'exécution et d'une sécurité de type pour des structures de données complexes dans des applications TypeScript.
Évitez Zod v4 si vous n'avez besoin que de contrôles nuls simples ou si votre projet n'utilise pas TypeScript.
Analyse de sécurité
SûrThe skill file contains only documentation and examples for a TypeScript validation library, with no executable commands, no external calls, and no destructive or data-exfiltrating instructions.
Aucun point d'attention détecté
Exemples
Create a Zod schema for a user with id (positive integer), email, username (3-20 chars, alphanumeric + underscore), age (13-120), and role (enum with default 'user'). Then parse an example object.Define a Zod discriminated union schema for API responses based on a 'status' field: 'success' with data, 'error' with error message and code, 'loading' with optional message. Then show how to parse a sample response.Create a Zod template literal schema that validates a version string like '1.2.3' (three numbers separated by dots). Then parse a valid and an invalid example.name: "Zod v4" description: "TypeScript-first schema validation library with static type inference and runtime validation" when_to_use: "When you need robust data validation, type safety, schema definitions, or input validation in TypeScript/JavaScript applications"
Zod v4 - Schema Validation Skill
Zod v4 is a TypeScript-first schema declaration and validation library that provides static type inference, runtime validation, and exceptional performance. Version 4 delivers 14x faster parsing and 66% smaller bundles while maintaining full type safety.
Quick Start
npm install zod@^4.0.0
import * as z from "zod";
// Basic schema creation and validation
const UserSchema = z.object({
id: z.number().int().positive(),
email: z.email(),
username: z.string().min(3).max(20),
age: z.number().int().min(18).optional(),
});
type User = z.infer<typeof UserSchema>;
// Parse and validate
const user = UserSchema.parse({
id: 1,
email: "user@example.com",
username: "johndoe",
age: 25,
});
// Safe parsing with error handling
const result = UserSchema.safeParse(input);
if (!result.success) {
console.log(result.error);
}
Common Patterns
Object Schemas with Validation
// Comprehensive user validation
const UserProfile = z.object({
id: z.number().int().positive(),
email: z.email(),
username: z
.string()
.min(3, "Username must be at least 3 characters")
.max(20, "Username cannot exceed 20 characters")
.regex(/^[a-zA-Z0-9_]+$/, "Only alphanumeric characters and underscores"),
age: z.number().int().min(13).max(120),
role: z.enum(["user", "admin", "moderator"]).default("user"),
bio: z.string().max(500).optional(),
website: z.url().optional(),
createdAt: z.date().default(() => new Date()),
});
// Extending schemas
const AdminProfile = UserProfile.extend({
permissions: z.array(z.string()),
accessLevel: z.number().min(1).max(10),
});
String Format Validation
// Built-in format validators
const Validations = {
email: z.email(),
uuid: z.uuidv4(),
url: z.url(),
ipv4: z.ipv4(),
ipv6: z.ipv6(),
base64: z.base64(),
jwt: z.jwt(),
// ISO formats
isoDate: z.iso.date(),
isoDateTime: z.iso.datetime(),
isoTime: z.iso.time(),
// Custom email with specific pattern
strictEmail: z.email({ pattern: z.regexes.rfc5322Email }),
};
// Template literal validation
const VersionString = z.templateLiteral([
z.number(),
".",
z.number(),
".",
z.number(),
]);
const CSSValue = z.templateLiteral([
z.number(),
z.enum(["px", "em", "rem", "%", "vh", "vw"]),
]);
Array and Collection Validation
// Array with constraints
const NumberArray = z.array(z.number()).min(1).max(100);
// Tuple validation
const Coordinates = z.tuple([z.number(), z.number()]);
const MixedTuple = z.tuple([z.string(), z.number()], z.boolean());
// Set validation
const UniqueStrings = z.set(z.string()).min(3);
// Map validation
const UserPermissions = z.map(
z.string(), // user ID
z.array(z.string()), // permissions
);
// Record validation
const StringToNumber = z.record(z.string(), z.number());
const StatusRecord = z.record(
z.enum(["pending", "active", "complete"]),
z.boolean(),
);
Union and Discriminated Unions
// Simple union
const StringOrNumber = z.union([z.string(), z.number()]);
// Discriminated union for API responses
const ApiResponse = z.discriminatedUnion("status", [
z.object({
status: z.literal("success"),
data: z.unknown(),
}),
z.object({
status: z.literal("error"),
error: z.string(),
code: z.number(),
}),
z.object({
status: z.literal("loading"),
message: z.string().optional(),
}),
]);
// Nested discriminated unions
const BaseError = z.object({
status: z.literal("error"),
message: z.string(),
});
const DetailedError = z.discriminatedUnion("code", [
BaseError.extend({ code: z.literal(400), field: z.string() }),
BaseError.extend({ code: z.literal(401), realm: z.string() }),
BaseError.extend({ code: z.literal(500), stack: z.string() }),
]);
Custom Refinements and Validation
// Custom validation with refinements
const PasswordSchema = z
.string()
.min(8, "Password must be at least 8 characters")
.refine((val) => /[A-Z]/.test(val), "Must contain uppercase letter")
.refine((val) => /[a-z]/.test(val), "Must contain lowercase letter")
.refine((val) => /[0-9]/.test(val), "Must contain number")
.refine((val) => /[^A-Za-z0-9]/.test(val), "Must contain special character");
// Complex validation with superRefine
const UserRegistration = z
.object({
email: z.email(),
password: z.string(),
confirmPassword: z.string(),
age: z.number(),
termsAccepted: z.boolean(),
})
.superRefine((data, ctx) => {
if (data.password !== data.confirmPassword) {
ctx.addIssue({
code: "custom",
path: ["confirmPassword"],
message: "Passwords must match",
});
}
if (data.age < 18 && !data.termsAccepted) {
ctx.addIssue({
code: "custom",
path: ["termsAccepted"],
message: "Parental consent required for users under 18",
});
}
});
Transformations and Data Processing
// Transform data during validation
const StringToNumber = z
.string()
.transform((val) => parseInt(val, 10))
.refine((val) => !isNaN(val), "Must be a valid number");
const TimestampToDate = z
.number()
.transform((timestamp) => new Date(timestamp));
const NormalizeEmail = z.string().transform((val) => val.toLowerCase().trim());
// Overwrite for type-preserving transforms
const RoundNumber = z.number().overwrite((val) => Math.round(val));
// Pipeline transformations
const ProcessUrl = z
.string()
.transform((val) => val.trim())
.transform((val) => val.toLowerCase())
.transform((val) => {
try {
return new URL(val);
} catch {
throw new Error("Invalid URL format");
}
});
Recursive Schemas
// Recursive category structure
const Category = z.object({
id: z.number(),
name: z.string(),
get subcategories() {
return z.array(Category);
},
});
// Mutually recursive types
const User = z.object({
id: z.number(),
name: z.string(),
get posts() {
return z.array(Post);
},
});
const Post = z.object({
id: z.number(),
title: z.string(),
content: z.string(),
get author() {
return User;
},
get comments() {
return z.array(Comment);
},
});
const Comment = z.object({
id: z.number(),
text: z.string(),
get author() {
return User.pick({ id: true, name: true });
},
});
File Validation
// File upload validation
const ImageUpload = z.object({
avatar: z
.file()
.max(5_000_000, "File must be less than 5MB")
.mime(["image/jpeg", "image/png", "image/webp"], "Must be an image"),
banner: z
.file()
.max(10_000_000, "File must be less than 10MB")
.mime(["image/jpeg", "image/png"], "Must be JPEG or PNG")
.optional(),
});
// Document validation
const DocumentUpload = z
.file()
.min(1000, "File must be at least 1KB")
.max(50_000_000, "File must be less than 50MB")
.mime([
"application/pdf",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
]);
Function Validation
// Define validated functions
const CalculateTax = z.function({
input: [z.number().min(0), z.number().min(0).max(1)],
output: z.number(),
});
const taxCalculator = CalculateTax.implement((amount, rate) => {
return amount * rate;
});
// Async function validation
const FetchUser = z.function({
input: [z.number().int().positive()],
output: z.object({
id: z.number(),
name: z.string(),
email: z.email(),
}),
});
const getUser = FetchUser.implementAsync(async (id) => {
const response = await fetch(`/api/users/${id}`);
return response.json();
});
Error Handling
// Custom error messages
const CustomValidation = z.object({
username: z
.string({
error: (issue) => {
if (issue.input === undefined) return "Username is required";
if (typeof issue.input !== "string") return "Username must be a string";
return "Invalid username";
},
})
.min(3, "Username must be at least 3 characters"),
});
// Error handling patterns
const validateInput = (input: unknown) => {
const result = UserSchema.safeParse(input);
if (!result.success) {
const error = result.error;
// Pretty print errors
console.log(z.prettifyError(error));
// Extract field errors
const fieldErrors = error.issues.reduce(
(acc, issue) => {
const field = issue.path.join(".");
acc[field] = issue.message;
return acc;
},
{} as Record<string, string>,
);
return { success: false, errors: fieldErrors };
}
return { success: true, data: result.data };
};
Default Values and Coercion
// Schema with defaults
const ConfigSchema = z.object({
theme: z.enum(["light", "dark"]).default("light"),
notifications: z.boolean().default(true),
fontSize: z.number().min(10).max(30).default(14),
timeout: z.number().default(5000),
});
// Type coercion
const CoercedConfig = z.object({
port: z.coerce.number().default(3000),
https: z.coerce.boolean().default(false),
maxConnections: z.coerce.number().int().positive().default(100),
});
// Environment variable parsing
const EnvSchema = z.object({
NODE_ENV: z
.enum(["development", "production", "test"])
.default("development"),
PORT: z.coerce.number().default(3000),
DEBUG: z.stringbool().default("false"),
});
Zod Mini (Tree-Shakable)
import * as z from "zod/mini";
// Functional API for smaller bundles
const OptionalString = z.optional(z.string());
const StringArray = z.array(z.string());
const StringOrNumber = z.union([z.string(), z.number()]);
// Check functions for validations
const ValidatedEmail = z
.string()
.check(z.regex(/@/), z.minLength(5), z.maxLength(100));
const PositiveInt = z.number().check(z.int(), z.positive(), z.lt(1000));
const NonEmptyArray = z.array(z.any()).check(z.minSize(1));
Practical Examples
Form Validation
// Contact form validation
const ContactForm = z.object({
name: z.string().min(1, "Name is required").max(100),
email: z.email("Please provide a valid email"),
subject: z.string().min(5, "Subject must be at least 5 characters"),
message: z.string().min(10, "Message must be at least 10 characters"),
newsletter: z.boolean().default(false),
});
// React form integration
const handleSubmit = (formData: FormData) => {
const data = {
name: formData.get("name"),
email: formData.get("email"),
subject: formData.get("subject"),
message: formData.get("message"),
newsletter: formData.get("newsletter") === "on",
};
const result = ContactForm.safeParse(data);
if (!result.success) {
const errors = result.error.flatten();
return { errors: errors.fieldErrors };
}
// Process valid data
return { success: true, data: result.data };
};
API Response Validation
// API response schemas
const UserResponse = z.object({
data: z.object({
id: z.number(),
name: z.string(),
email: z.email(),
createdAt: z.string().transform((val) => new Date(val)),
}),
meta: z.object({
total: z.number(),
page: z.number(),
totalPages: z.number(),
}),
});
// Typed API client
const apiClient = {
async getUser(id: number): Promise<z.infer<typeof UserResponse>["data"]> {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
const result = UserResponse.safeParse(data);
if (!result.success) {
throw new Error(`Invalid API response: ${result.error.message}`);
}
return result.data.data;
},
};
Configuration Validation
// Application configuration
const AppConfig = z.object({
server: z.object({
port: z.coerce.number().min(1).max(65535).default(3000),
host: z.string().default("localhost"),
cors: z.boolean().default(true),
}),
database: z.object({
url: z.string(),
ssl: z.boolean().default(false),
maxConnections: z.coerce.number().int().positive().default(10),
}),
auth: z.object({
jwtSecret: z.string().min(32),
tokenExpiry: z.string().default("24h"),
refreshExpiry: z.string().default("7d"),
}),
});
// Load and validate config
const loadConfig = (configPath: string) => {
const rawConfig = require(configPath);
const config = AppConfig.parse(rawConfig);
return config;
};
Requirements
- TypeScript: 4.5+ (recommended for best inference)
- Runtime: Node.js, browsers, Deno, Bun
- Bundle size: 5.36kb gzipped (full), 1.88kb gzipped (mini)
Installation
# Full Zod v4
npm install zod@^4.0.0
# For minimal bundle size
npm install zod@^4.0.0
# Then import from "zod/mini"
Key Features
- Static Type Inference: Automatic TypeScript type generation from schemas
- Runtime Validation: Comprehensive input validation with detailed errors
- Performance: 14x faster parsing than v3, optimized for production
- Tree Shakable: Zod Mini provides 85% bundle size reduction
- Template Literals: Validate string patterns matching TypeScript template literals
- File Validation: Built-in File object validation with size and MIME type constraints
- Recursive Types: Full support for recursive and self-referential schemas
- JSON Schema: First-party JSON Schema generation
- Function Validation: Type-safe function definitions with validated inputs/outputs
Expert Next.js App Router
Developpement
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Developpement
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Developpement
Génère de la documentation API complète au format OpenAPI/Swagger.