Zod v4 Validation

Guidance for using Zod v4 correctly, including the required import path, schema patterns, and type inference in TypeScript projects.

AuthorNeexoCore
Apply to**/*.{ts,tsx}
Updated
zodvalidationtypescript

Overview

Zod v4 changed the import path. Projects using Zod v4 must import from "zod/v4", never from "zod" directly.

Critical Import Rule

// ✅ Correct — Zod v4
import { z } from "zod/v4";

// ❌ Wrong — imports Zod v3 API even if v4 is installed
import { z } from "zod";

This is the single most common mistake in Zod v4 codebases and causes subtle runtime issues.

Schema Patterns

import { z } from "zod/v4";

// Define schemas
const UserSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  name: z.string().min(1).max(100),
  role: z.enum(["admin", "editor", "viewer"]),
  createdAt: z.date(),
});

// Infer TypeScript type from schema
type User = z.infer<typeof UserSchema>;

// Record requires two arguments in Zod v4
const MetadataSchema = z.record(z.string(), z.unknown());

Validator Organization

  • One validator file per domain: src/lib/validators/users.ts, src/lib/validators/orders.ts
  • Export named schemas and their inferred types together
  • Keep schemas close to where they are used (actions, API routes, forms)

Server Action Pattern

import { z } from "zod/v4";

const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1),
});

export async function createUser(input: unknown) {
  const parsed = CreateUserSchema.safeParse(input);
  if (!parsed.success) {
    return { success: false, error: parsed.error.message };
  }
  // Use parsed.data — fully typed
}

Common Pitfalls

  • z.record() requires two arguments in Zod v4: z.record(z.string(), z.unknown()) — one argument throws
  • z.coerce.date() parses strings to dates — useful for form inputs
  • Do not mix Zod v3 and v4 imports in the same project

Raw content

Copy this into your project — e.g. .instructions.md, .agent.md, or SKILL.md

## Overview

Zod v4 changed the import path. Projects using Zod v4 **must** import from `"zod/v4"`, never from `"zod"` directly.

## Critical Import Rule

```tsx
// ✅ Correct — Zod v4
import { z } from "zod/v4";

// ❌ Wrong — imports Zod v3 API even if v4 is installed
import { z } from "zod";
```

This is the single most common mistake in Zod v4 codebases and causes subtle runtime issues.

## Schema Patterns

```tsx
import { z } from "zod/v4";

// Define schemas
const UserSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  name: z.string().min(1).max(100),
  role: z.enum(["admin", "editor", "viewer"]),
  createdAt: z.date(),
});

// Infer TypeScript type from schema
type User = z.infer<typeof UserSchema>;

// Record requires two arguments in Zod v4
const MetadataSchema = z.record(z.string(), z.unknown());
```

## Validator Organization

- One validator file per domain: `src/lib/validators/users.ts`, `src/lib/validators/orders.ts`
- Export named schemas and their inferred types together
- Keep schemas close to where they are used (actions, API routes, forms)

## Server Action Pattern

```tsx
import { z } from "zod/v4";

const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1),
});

export async function createUser(input: unknown) {
  const parsed = CreateUserSchema.safeParse(input);
  if (!parsed.success) {
    return { success: false, error: parsed.error.message };
  }
  // Use parsed.data — fully typed
}
```

## Common Pitfalls

- `z.record()` requires two arguments in Zod v4: `z.record(z.string(), z.unknown())` — one argument throws
- `z.coerce.date()` parses strings to dates — useful for form inputs
- Do not mix Zod v3 and v4 imports in the same project