Claude Code Plugins

Community-maintained marketplace

Feedback

Zod 4+ validation patterns. Use when validating forms, API responses, env vars, or inferring types from schemas.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name zod
description Zod 4+ validation patterns. Use when validating forms, API responses, env vars, or inferring types from schemas.

Zod 4+

String formats — top-level

// v4: top-level functions
const email = z.email()
const uuid = z.uuid()
const url = z.url()
const iso = z.iso.datetime()

// NOT z.string().email() — that's v3

Record — two arguments required

// v4: key + value schemas
z.record(z.string(), z.number())

// NOT z.record(z.number()) — that's v3

Objects

// Strict (no extra keys)
z.strictObject({ name: z.string() })

// Loose (allow extra keys)
z.looseObject({ name: z.string() })

// .strict() and .passthrough() still work but prefer above

Error customization

// v4: unified 'error' param
z.string({ error: "Must be a string" })
z.email({ error: (issue) => `Invalid: ${issue.input}` })

// NOT 'message', 'invalid_type_error', etc. — that's v3

Metadata

// v4: .meta() with object
z.string().meta({ label: "Username", description: "Your handle" })

// NOT .describe() — that's v3

Unions and intersections

// v4: prefer explicit helpers
z.union([z.string(), z.number()])
z.intersection(baseSchema, extendSchema)

// NOT .or() / .and() chains — those are v3 style

Accessing errors

const result = schema.safeParse(data)
if (!result.success) {
  console.log(result.error.issues)  // v4: .issues
  // NOT .errors — that's v3
}

Form validation with React 19

const formSchema = z.object({
  email: z.email({ error: "Invalid email" }),
  age: z.coerce.number().min(18, { error: "Must be 18+" })
})

async function action(prev: State, formData: FormData) {
  const result = formSchema.safeParse(Object.fromEntries(formData))
  if (!result.success) {
    return { errors: result.error.issues }
  }
  // result.data is typed
}

Type inference

const userSchema = z.object({
  id: z.uuid(),
  email: z.email(),
  role: z.enum(["admin", "user"])
})

type User = z.infer<typeof userSchema>

Avoid

  • z.string().email()z.email()
  • z.string().uuid()z.uuid()
  • z.record(value)z.record(key, value)
  • message param → error param
  • .describe().meta()
  • .or() / .and()z.union() / z.intersection()
  • .errors.issues