
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-4Add your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1k |
|---|---|
| repo stars | ★ 14.5k |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | prowler-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
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.