Skip to content
Development
Skill

/web-forms-zod-validation

Zod schema validation patterns for TypeScript - schema definitions, type inference, refinements, transforms, discriminated unions

From plugin
skills
26200 skills

Claude Code

$ npx -y skills add agents-inc/skills --skill web-forms-zod-validation --agent claude-code

SKILL.md

web-forms-zod-validation.SKILL.md
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:** A schema is declared once and the TypeScript type derived from it with `z.infer`, > so the rule and the type cannot drift. `safeParse` returns a result object where invalid input is > expected and `parse` throws where it is a bug; `refine` and `superRefine` carry rules the built-in > checks cannot express, and `transform` converts during validation — which splits `z.input` from > `z.output`. On v4 the string formats moved to the top level (`z.email()`, `z.url()`, `z.iso.*`) and > the v3 method chains are deprecated rather than removed; `flatten()`, `format()` and `merge()` have > replacements, and [reference.md](reference.md) carries the full migration list.

**Detailed Resources:**

  • [examples/core.md](examples/core.md) — schema definition, safe parsing, error formatting, unions, composition, async refinements
  • [examples/transforms.md](examples/transforms.md) — transforms, coercion, pipe chains, query params
  • [examples/advanced-patterns.md](examples/advanced-patterns.md) — branded types, `.catch()` fallbacks, readonly, recursive schemas
  • [reference.md](reference.md) — decision trees, method lookup, worked anti-patterns, v4 migration guide

---

Which path applies

  • **The schema checks a value and hands back what it received** — `z.infer` is the only type helper

needed, and the parsed value has the shape the caller passed in. Follow [examples/core.md](examples/core.md).

  • **The schema converts as it checks** — a `transform`, a `coerce` or a `default` makes the input and

output types differ, so a function taking pre-validation data types its parameter `z.input` and its return `z.output`. Follow [examples/transforms.md](examples/transforms.md).

---

<critical_requirements>

Before writing Zod schemas

**Reach for `safeParse` wherever invalid input is expected.** It returns a result object rather than throwing, so the failure is a branch rather than a catch, and `result.error.issues` carries the field paths a form needs. Keep `parse` for config and internal data, where invalid means a bug.

**Derive the type with `z.infer<typeof schema>`.** A hand-written interface beside a schema is a second declaration of the same thing, and the two drift in the direction that leaves the type claiming more than the schema checks.

**Validate where untrusted data enters** — API responses, form input, config, URL params. A shape change caught at the boundary names the field that moved; caught later it surfaces as an undefined property several frames away.

**Name the validation limits.** `.min(MIN_USERNAME_LENGTH)` says what the number is for, and the same constant reaches the error message so the two cannot disagree.

</critical_requirements>

---

**Auto-detection:** zod, z.object, z.infer, z.input, z.output, safeParse, safeParseAsync, parse, parseAsync, refine, superRefine, ctx.addIssue, transform, discriminatedUnion, z.coerce, z.pipe, z.catch, z.brand, z.lazy, z.email, z.url, z.uuid, z.iso, z.flattenError, z.treeifyError, z.strictObject, z.looseObject, ZodError

**Applies to:**

  • Validating data crossing a trust boundary, and reporting which field failed
  • Deriving TypeScript types from the rules that enforce them
  • Cross-field rules, conditional shapes and discriminated variants
  • Converting values during validation — strings to numbers, ISO strings to `Date`
  • Composing schemas for the read, create and update shapes of one record

**Handled elsewhere:**

  • Wiring a schema into a form — a form library accepts one through its own adapter or validator

slot, and how that connection is made is settled by whatever owns it.

  • Where the data came from — a schema validates a value it is handed and performs no I/O of its own.
  • Persistence schemas — a runtime validator and a table definition are separate artefacts even where

they describe the same record, and generating one from the other is that tool's concern.

---

<philosophy>

TypeScript checks the code you compile; a schema checks the data you receive. The two meet at the boundary, and the point of deriving the type from the schema is that only one of them can be wrong.

const UserSchema = z.object({ name: z.string(), email: z.email() });
type User = z.infer<typeof UserSchema>;

Written the other way round — an interface, and a schema maintained beside it — a field added to the interface and forgotten in the schema type-checks everywhere while validating nothing.

The corollary is where _not_ to reach for a schema. Data that has already crossed a boundary has been checked, and re-validating it inside a function the compiler already governs buys nothing and costs a parse on every call.

</philosophy>

---

<patterns>

Core patterns

Pattern 1: Schema with named limits

The constant appears in the check and in the message, so a change to the limit updates both.

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.email("Invalid email format"),
});

type User = z.infer<typeof UserSchema>;

Full code: [examples/core.md](examples/core.md) Pattern 1

---

Pattern 2: safeParse and error formatting

The result is a discriminated union on `success`, so the failure branch narrows to an error and the success branch to typed data.

const result = UserSchema.safeParse(data);

if (!result.success) {
  const { fieldErrors } = z.flattenError(result.error);
  return { success: false, errors: fieldErrors };
}

return { success: true, user: result.data };
Read more
@agents-inc
Ships withskills

The official skills marketplace for Agents Inc. 230+ 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?

Stars on GitHub

Momentum

stars in total
26
Star history of agents-inc/skills on GitHub, a chart by star-history.com

Other skills on skills.

ai-infrastructure-litellm

ai-infrastructure-lite…

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking,…

@agents-inc@agents-incInstall Skill
ai-infrastructure-modal

ai-infrastructure-moda…

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client…

@agents-inc@agents-incInstall Skill
ai-infrastructure-ollama

ai-infrastructure-olla…

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output,…

@agents-inc@agents-incInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about web-forms-zod-validation.

Which coding agents can use web-forms-zod-validation?
web-forms-zod-validation installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.