/web-forms-zod-validation
Zod schema validation patterns for TypeScript - schema definitions, type inference, refinements, transforms, discriminated unions
$ npx -y skills add agents-inc/skills --skill web-forms-zod-validation --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/web-forms-zod-validation
Context preview
The summary Claude sees to decide when to auto-load this skill.
Zod schema validation patterns for TypeScript - schema definitions, type inference, refinements, transforms, discriminated unions
SKILL.md
web-forms-zod-validation.SKILL.mdname: web-forms-zod-validation
description: Zod schema validation patterns for TypeScript - schema definitions, type inference, refinements, transforms, discriminated unions
Zod Schema Validation Patterns
> **Quick Guide:** Use Zod for runtime validation at trust boundaries (API responses, form inputs, config, URL params). Define schemas once, derive types with `z.infer`. Use `safeParse` for error handling, `refine`/`superRefine` for custom validation, `transform` for data conversion. Named constants for all validation limits. > > **Version Note:** Zod v4 is now the stable release (v4.1+). It brings 14.7x faster string parsing, 57% smaller bundle, and new top-level APIs (`z.email()`, `z.url()`, `z.iso.*`). The v3 method-chain equivalents (`z.string().email()`) still work but are deprecated. For migration details, see [reference.md](reference.md).
---
<critical_requirements>
CRITICAL: Before Using This Skill
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST use `safeParse` instead of `parse` for user-facing validation - prevents unhandled exceptions)**
**(You MUST use `z.infer<typeof schema>` to derive types - never duplicate schema as separate interface)**
**(You MUST validate at trust boundaries - API responses, form inputs, config files, URL params)**
**(You MUST use named constants for validation limits - NO magic numbers in `.min()`, `.max()`, `.length()`)**
</critical_requirements>
---
**Auto-detection:** Zod schemas, z.object, z.string, z.number, z.infer, safeParse, refine, superRefine, transform, discriminatedUnion, z.coerce, z.pipe, z.catch, z.brand, z.lazy, z.email, z.url, z.iso
**When to use:**
- Validating API responses before using data
- Parsing form input data with type safety
- Validating configuration files or environment variables
- Defining contracts between systems (frontend/backend shared schemas)
- Runtime type checking for data from untrusted sources
**When NOT to use:**
- Internal function parameters (TypeScript is sufficient for trusted data)
- Simple boolean checks that don't need schema definition
- Performance-critical hot paths where validation overhead matters
---
<philosophy>
Philosophy
TypeScript provides compile-time type safety for code you control. Zod provides **runtime validation** for data you don't control - API responses, user input, configuration files, URL parameters. Use TypeScript for internal contracts; use Zod at **trust boundaries** where external data enters your system.
**Key principle:** Define the schema once, derive the type. Never maintain parallel type definitions and validation logic - they will drift apart.
// Schema is the source of truth
const UserSchema = z.object({
name: z.string(),
email: z.string().email(),
});
// Type is derived, always in sync
type User = z.infer<typeof UserSchema>;</philosophy>
---
<patterns>
Core Patterns
Pattern 1: Schema Definition with Named Constants
Define schemas with named constants for all validation limits. Custom error messages for user-facing fields.
const MIN_USERNAME_LENGTH = 3;
const MAX_USERNAME_LENGTH = 50;
const UserSchema = z.object({
username: z
.string()
.min(
MIN_USERNAME_LENGTH,
`Username must be at least ${MIN_USERNAME_LENGTH} characters`,
)
.max(
MAX_USERNAME_LENGTH,
`Username cannot exceed ${MAX_USERNAME_LENGTH} characters`,
),
email: z.string().email("Invalid email format"),
});
type User = z.infer<typeof UserSchema>; // Always derived, never manual interface**Why good:** named constants make limits discoverable, custom error messages improve UX, type derived from schema
See [examples/core.md](examples/core.md) for complete schema examples with reusable sub-schemas and CRUD composition patterns.
---
Pattern 2: Safe Parsing for Error Handling
Use `safeParse` for user input and API responses. Reserve `parse` for config/internal data where invalid = programming error.
const result = UserSchema.safeParse(data);
if (!result.success) {
const errors = result.error.issues.reduce(
(acc, err) => {
const field = err.path.join(".");
acc[field] = err.message;
return acc;
},
{} as Record<string, string>,
);
return { success: false, errors };
}
return { success: true, user: result.data };**Why good:** safeParse never throws, validation errors handled explicitly, error formatting provides useful field-level feedback
See [examples/core.md](examples/core.md) for form validation and API response validation patterns.
---
Pattern 3: Refinements and Cross-Field Validation
Use `refine` for custom validation logic. Use `superRefine` when you need cross-field validation with specific error paths.
const MIN_PASSWORD_LENGTH = 8;
const PasswordFormSchema = z
.object({
password: z.string().min(MIN_PASSWORD_LENGTH),
confirmPassword: z.string(),
})
.superRefine((data, ctx) => {
if (data.password !== data.confirmPassword) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Passwords do not match",
path: ["confirmPassword"],
});
}
});**Why good:** superRefine enables cross-field validation with specific error paths, keeps all validation in the schema
See [examples/core.md](examples/core.md) for password refinement chains and conditional validation patterns.
---
Pattern 4: Transforms and Type Conversion
Use `transform` to convert data during validation. Use `z.input` and `z.output` when transforms change the type.
const DateSchema = z
.string()
.datetime()
.transform((str) => new Date(str));
type DateInput = z.input<typeof DateSchema>; // string
type DateOutput = z.output<typeof DateSchema>; // Date
**Gotcha:** `z.infer` returns the output type. When a function accepts pre-validati
Read more
name: web-forms-zod-validation description: Zod schema validation patterns for TypeScript - schema definitions, type inference, refinements, transforms, discriminated unions
Zod Schema Validation Patterns
> **Quick Guide:** Use Zod for runtime validation at trust boundaries (API responses, form inputs, config, URL params). Define schemas once, derive types with `z.infer`. Use `safeParse` for error handling, `refine`/`superRefine` for custom validation, `transform` for data conversion. Named constants for all validation limits. > > **Version Note:** Zod v4 is now the stable release (v4.1+). It brings 14.7x faster string parsing, 57% smaller bundle, and new top-level APIs (`z.email()`, `z.url()`, `z.iso.*`). The v3 method-chain equivalents (`z.string().email()`) still work but are deprecated. For migration details, see [reference.md](reference.md).
---
<critical_requirements>
CRITICAL: Before Using This Skill
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST use `safeParse` instead of `parse` for user-facing validation - prevents unhandled exceptions)**
**(You MUST use `z.infer<typeof schema>` to derive types - never duplicate schema as separate interface)**
**(You MUST validate at trust boundaries - API responses, form inputs, config files, URL params)**
**(You MUST use named constants for validation limits - NO magic numbers in `.min()`, `.max()`, `.length()`)**
</critical_requirements>
---
**Auto-detection:** Zod schemas, z.object, z.string, z.number, z.infer, safeParse, refine, superRefine, transform, discriminatedUnion, z.coerce, z.pipe, z.catch, z.brand, z.lazy, z.email, z.url, z.iso
**When to use:**
- Validating API responses before using data
- Parsing form input data with type safety
- Validating configuration files or environment variables
- Defining contracts between systems (frontend/backend shared schemas)
- Runtime type checking for data from untrusted sources
**When NOT to use:**
- Internal function parameters (TypeScript is sufficient for trusted data)
- Simple boolean checks that don't need schema definition
- Performance-critical hot paths where validation overhead matters
---
<philosophy>
Philosophy
TypeScript provides compile-time type safety for code you control. Zod provides **runtime validation** for data you don't control - API responses, user input, configuration files, URL parameters. Use TypeScript for internal contracts; use Zod at **trust boundaries** where external data enters your system.
**Key principle:** Define the schema once, derive the type. Never maintain parallel type definitions and validation logic - they will drift apart.
// Schema is the source of truth
const UserSchema = z.object({
name: z.string(),
email: z.string().email(),
});
// Type is derived, always in sync
type User = z.infer<typeof UserSchema>;</philosophy>
---
<patterns>
Core Patterns
Pattern 1: Schema Definition with Named Constants
Define schemas with named constants for all validation limits. Custom error messages for user-facing fields.
const MIN_USERNAME_LENGTH = 3;
const MAX_USERNAME_LENGTH = 50;
const UserSchema = z.object({
username: z
.string()
.min(
MIN_USERNAME_LENGTH,
`Username must be at least ${MIN_USERNAME_LENGTH} characters`,
)
.max(
MAX_USERNAME_LENGTH,
`Username cannot exceed ${MAX_USERNAME_LENGTH} characters`,
),
email: z.string().email("Invalid email format"),
});
type User = z.infer<typeof UserSchema>; // Always derived, never manual interface**Why good:** named constants make limits discoverable, custom error messages improve UX, type derived from schema
See [examples/core.md](examples/core.md) for complete schema examples with reusable sub-schemas and CRUD composition patterns.
---
Pattern 2: Safe Parsing for Error Handling
Use `safeParse` for user input and API responses. Reserve `parse` for config/internal data where invalid = programming error.
const result = UserSchema.safeParse(data);
if (!result.success) {
const errors = result.error.issues.reduce(
(acc, err) => {
const field = err.path.join(".");
acc[field] = err.message;
return acc;
},
{} as Record<string, string>,
);
return { success: false, errors };
}
return { success: true, user: result.data };**Why good:** safeParse never throws, validation errors handled explicitly, error formatting provides useful field-level feedback
See [examples/core.md](examples/core.md) for form validation and API response validation patterns.
---
Pattern 3: Refinements and Cross-Field Validation
Use `refine` for custom validation logic. Use `superRefine` when you need cross-field validation with specific error paths.
const MIN_PASSWORD_LENGTH = 8;
const PasswordFormSchema = z
.object({
password: z.string().min(MIN_PASSWORD_LENGTH),
confirmPassword: z.string(),
})
.superRefine((data, ctx) => {
if (data.password !== data.confirmPassword) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Passwords do not match",
path: ["confirmPassword"],
});
}
});**Why good:** superRefine enables cross-field validation with specific error paths, keeps all validation in the schema
See [examples/core.md](examples/core.md) for password refinement chains and conditional validation patterns.
---
Pattern 4: Transforms and Type Conversion
Use `transform` to convert data during validation. Use `z.input` and `z.output` when transforms change the type.
const DateSchema = z .string() .datetime() .transform((str) => new Date(str)); type DateInput = z.input<typeof DateSchema>; // string type DateOutput = z.output<typeof DateSchema>; // Date
**Gotcha:** `z.infer` returns the output type. When a function accepts pre-validati
Showing the first part of this file.
The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?
Repo: agents-inc/skills
Other skills on agents-inc-skills.
- /ai-infrastructure-huggingface-inference
Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints
Open skill - /ai-infrastructure-litellm
LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment
Open skill - /ai-infrastructure-modal
Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns
Open skill - /ai-infrastructure-ollama
Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint
Open skill - /ai-infrastructure-replicate
Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training
Open skill - /ai-infrastructure-together-ai
Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints
Open skill

