Provides patterns and syntax for Zod 4 schema validation and v3 to v4 migrations.

Install

mkdir -p .claude/skills/zod-4 && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/894" && unzip -o skill.zip -d .claude/skills/zod-4 && rm skill.zip

Installs to .claude/skills/zod-4

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Zod 4 schema validation patterns. Trigger: When creating or updating Zod v4 schemas for validation/parsing (forms, request payloads, adapters), including v3 -> v4 migration patterns.
182 chars · catalog description✓ has a “when” trigger
Intermediate

Key capabilities

  • Define schema validation for primitives, objects, arrays, and tuples
  • Implement discriminated unions for efficient type checking
  • Apply transformations, coercions, and preprocessing during parsing
  • Configure custom error messages using the error parameter
  • Integrate with React Hook Form using zodResolver
  • Apply refinements and superRefine for complex validation logic

How it works

The skill provides patterns for defining Zod 4 schemas that use parse or safeParse methods to validate input data against defined constraints. It utilizes specific Zod 4 syntax such as top-level validators and the updated error parameter structure.

Inputs & outputs

You give it
Raw data object or value
You get back
Parsed data or validation error issues

When to use zod-4

  • Migrating legacy Zod 3 schemas to Zod 4
  • Defining validation for API request payloads
  • Creating reusable form validation schemas
  • Setting up custom error messages for input fields

About this skill

Breaking Changes from Zod 3

// ❌ Zod 3 (OLD)
z.string().email()
z.string().uuid()
z.string().url()
z.string().nonempty()
z.object({ name: z.string() }).required_error("Required")

// ✅ Zod 4 (NEW)
z.email()
z.uuid()
z.url()
z.string().min(1)
z.object({ name: z.string() }, { error: "Required" })

Basic Schemas

import { z } from "zod";

// Primitives
const stringSchema = z.string();
const numberSchema = z.number();
const booleanSchema = z.boolean();
const dateSchema = z.date();

// Top-level validators (Zod 4)
const emailSchema = z.email();
const uuidSchema = z.uuid();
const urlSchema = z.url();

// With constraints
const nameSchema = z.string().min(1).max(100);
const ageSchema = z.number().int().positive().max(150);
const priceSchema = z.number().min(0).multipleOf(0.01);

Object Schemas

const userSchema = z.object({
  id: z.uuid(),
  email: z.email({ error: "Invalid email address" }),
  name: z.string().min(1, { error: "Name is required" }),
  age: z.number().int().positive().optional(),
  role: z.enum(["admin", "user", "guest"]),
  metadata: z.record(z.string(), z.unknown()).optional(),
});

type User = z.infer<typeof userSchema>;

// Parsing
const user = userSchema.parse(data);  // Throws on error
const result = userSchema.safeParse(data);  // Returns { success, data/error }

if (result.success) {
  console.log(result.data);
} else {
  console.log(result.error.issues);
}

Arrays and Records

// Arrays
const tagsSchema = z.array(z.string()).min(1).max(10);
const numbersSchema = z.array(z.number()).nonempty();

// Records (objects with dynamic keys)
const scoresSchema = z.record(z.string(), z.number());
// { [key: string]: number }

// Tuples
const coordinatesSchema = z.tuple([z.number(), z.number()]);
// [number, number]

Unions and Discriminated Unions

// Simple union
const stringOrNumber = z.union([z.string(), z.number()]);

// Discriminated union (more efficient)
const resultSchema = z.discriminatedUnion("status", [
  z.object({ status: z.literal("success"), data: z.unknown() }),
  z.object({ status: z.literal("error"), error: z.string() }),
]);

Transformations

// Transform during parsing
const lowercaseEmail = z.email().transform(email => email.toLowerCase());

// Coercion (convert types)
const numberFromString = z.coerce.number();  // "42" → 42
const dateFromString = z.coerce.date();      // "2024-01-01" → Date

// Preprocessing
const trimmedString = z.preprocess(
  val => typeof val === "string" ? val.trim() : val,
  z.string()
);

Refinements

const passwordSchema = z.string()
  .min(8)
  .refine(val => /[A-Z]/.test(val), {
    message: "Must contain uppercase letter",
  })
  .refine(val => /[0-9]/.test(val), {
    message: "Must contain number",
  });

// With superRefine for multiple errors
const formSchema = z.object({
  password: z.string(),
  confirmPassword: z.string(),
}).superRefine((data, ctx) => {
  if (data.password !== data.confirmPassword) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: "Passwords don't match",
      path: ["confirmPassword"],
    });
  }
});

Optional and Nullable

// Optional (T | undefined)
z.string().optional()

// Nullable (T | null)
z.string().nullable()

// Both (T | null | undefined)
z.string().nullish()

// Default values
z.string().default("unknown")
z.number().default(() => Math.random())

Error Handling

// Zod 4: Use 'error' param instead of 'message'
const schema = z.object({
  name: z.string({ error: "Name must be a string" }),
  email: z.email({ error: "Invalid email format" }),
  age: z.number().min(18, { error: "Must be 18 or older" }),
});

// Custom error map
const customSchema = z.string({
  error: (issue) => {
    if (issue.code === "too_small") {
      return "String is too short";
    }
    return "Invalid string";
  },
});

React Hook Form Integration

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const schema = z.object({
  email: z.email(),
  password: z.string().min(8),
});

type FormData = z.infer<typeof schema>;

function Form() {
  const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
    resolver: zodResolver(schema),
  });

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email")} />
      {errors.email && <span>{errors.email.message}</span>}
    </form>
  );
}

When not to use it

  • Validating data structures that do not require runtime type safety
  • Handling non-JavaScript environments without Zod support

Limitations

  • Requires migration of legacy Zod 3 syntax like required_error to Zod 4 standards
  • Validation logic is restricted to the capabilities of the Zod 4 library

How it compares

Unlike manual validation logic, this approach uses declarative schema definitions to automatically handle type inference and error reporting.

Compared to similar skills

zod-4 side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
zod-4 (this skill)127moNo flagsIntermediate
develop-ai-functions-example56moReviewIntermediate
lint-and-validate66moReviewBeginner
indicator-buffer11moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

develop-ai-functions-example

vercel

Develop examples for AI SDK functions. Use when creating, running, or modifying examples under examples/ai-functions/src to validate provider support, demonstrate features, or create test fixtures.

536

lint-and-validate

davila7

Automatic quality control, linting, and static analysis procedures. Use after every code modification to ensure syntax correctness and project standards. Triggers onKeywords: lint, format, check, validate, types, static analysis.

68

indicator-buffer

DaveSkender

Implement BufferList incremental indicators with efficient state management. Use for IIncrementFromChain or IIncrementFromQuote implementations. Covers interface selection, constructor patterns, and BufferListTestBase testing requirements.

13

agent-implementer-sparc-coder

ruvnet

Agent skill for implementer-sparc-coder - invoke with $agent-implementer-sparc-coder

11

perplexity-local-dev-loop

jeremylongshore

Configure Perplexity local development with hot reload and testing. Use when setting up a development environment, configuring test workflows, or establishing a fast iteration cycle with Perplexity. Trigger with phrases like "perplexity dev setup", "perplexity local development", "perplexity dev environment", "develop with perplexity".

11

add-policy

microsoft

Use when adding, modifying, or reviewing VS Code configuration policies. Covers the full policy lifecycle from registration to export to platform-specific artifacts. Run on ANY change that adds a `policy:` field to a configuration property.

00

Search skills

Search the agent skills registry