Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
prowler-cloud avatar

Zod 4

  • 1k installs
  • 14.5k repo stars
  • Updated August 4, 2026
  • prowler-cloud/prowler

zod-4 is an agent skill that generates correct Zod v4 schemas and migrates existing Zod v3 schemas for form, payload, and adapter validation without syntax errors.

About

zod-4 is a prowler-cloud agent skill (version 1.0, Apache-2.0) scoped to root and UI projects with auto-invoke on Zod schema creation. The skill documents Zod 4 breaking changes: top-level z.email(), z.uuid(), and z.url() replace z.string().email() chains; z.string().min(1) replaces nonempty(); and object error params use { error: "Required" } instead of required_error. Coverage includes object schemas with z.infer, arrays, records, tuples, discriminated unions, transforms, coerce, refinements, superRefine, optional/nullable/nullish defaults, and React Hook Form zodResolver integration. Developers reach for zod-4 when upgrading prowler UI or any TypeScript codebase from Zod 3 to Zod 4 or authoring new validators for forms and API adapters.

  • Complete Zod 4 migration patterns from v3
  • Top-level validators: z.email(), z.uuid(), z.url()
  • Object schema syntax with per-field error messages
  • Primitive and constrained schema examples for forms and APIs
  • Includes both basic and advanced validation patterns

Zod 4 by the numbers

  • 1,018 all-time installs (skills.sh)
  • +42 installs in the week ending Aug 5, 2026 (Skillselion tracking)
  • Ranked #377 of 2,245 Frontend Development skills by installs in the Skillselion catalog
  • Security screen: MEDIUM risk (skills.sh audit)
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/prowler-cloud/prowler --skill zod-4

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1k
repo stars14.5k
Security audit3 / 3 scanners passed
Last updatedAugust 4, 2026
Repositoryprowler-cloud/prowler

How do you migrate Zod v3 schemas to Zod v4?

Generate correct Zod v4 schemas and migrate existing v3 schemas without syntax errors.

Who is it for?

TypeScript developers upgrading to Zod v4 or adding form and API payload validators with React Hook Form integration.

Skip if: Python pydantic validation, Joi schemas, or codebases staying on Zod 3 without migration plans.

When should I use this skill?

The user creates or updates Zod schemas, migrates v3 to v4, or wires zodResolver for React Hook Form validation.

What you get

Valid Zod v4 TypeScript schemas with parse/safeParse handlers and optional React Hook Form zodResolver wiring.

  • Zod v4 schema modules
  • migration diff for v3 patterns
  • zodResolver form wiring

By the numbers

  • Skill metadata version 1.0
  • Scoped to root and UI projects with auto-invoke on schema creation

Files

SKILL.mdMarkdownGitHub ↗

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>
  );
}

Related skills

How it compares

Use zod-4 specifically for Zod v4 syntax and migration; use generic TypeScript skills when validation libraries are not Zod.

FAQ

What changed from Zod 3 to Zod 4 in zod-4?

zod-4 documents Zod 4 breaking changes: z.email(), z.uuid(), and z.url() are top-level validators; z.string().min(1) replaces nonempty(); object errors use { error: "..." } instead of required_error.

Does zod-4 cover React Hook Form?

zod-4 includes React Hook Form integration with zodResolver, showing useForm wired to Zod 4 object schemas and typed FormData via z.infer.

Is Zod 4 safe to install?

skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.