npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

eslint-plugin-zod-v4

v0.4.0

Published

ESLint plugin for Zod v4 best practices and migration from v3

Readme

eslint-plugin-zod-v4

ESLint plugin for Zod v4 best practices and migration from v3.

npm version License: MIT

Features

  • Detects deprecated Zod v3 patterns that break in v4
  • Enforces Zod v4 best practices
  • Auto-fix support for most rules
  • Educational error messages explaining the correct approach
  • Full ESLint 9+ flat config support
  • React-aware: Recognizes useMemo/useCallback for schema memoization

Installation

npm install --save-dev eslint-plugin-zod-v4

Requirements

  • ESLint >= 9.0.0 (supports ESLint 9.x and 10.x)
  • Node.js >= 20.19.0 (ESLint 10 requires Node 20.19+, 22.13+, or 24+)

Usage

ESLint 9+ / 10+ (Flat Config)

// eslint.config.js
import zodPlugin from "eslint-plugin-zod-v4"

export default [
  // Use recommended config (breaking changes only)
  zodPlugin.configs.recommended,

  // Or use strict config (recommended + best practices)
  // zodPlugin.configs.strict,

  // Or configure rules manually
  {
    plugins: {
      "zod-v4": zodPlugin,
    },
    rules: {
      "zod-v4/no-deprecated-string-format": "error",
      "zod-v4/prefer-safeParse": "warn",
    },
  },
]

Configs

| Config | Description | |--------|-------------| | recommended | Breaking changes only (errors). Use to catch code that will break in Zod v4. | | strict | Recommended + best practices (warnings). Enforces optimal Zod v4 patterns. | | all | All rules enabled as errors. Maximum strictness. |

Rules

Breaking Changes (severity: error)

These rules detect Zod v3 patterns that will break in v4.

| Rule | Description | Fixable | |------|-------------|---------| | no-deprecated-string-format | Disallow z.string().email() etc. Use z.email() instead. | ✅ Yes | | no-record-single-arg | Require z.record(keySchema, valueSchema) with two arguments. | ❌ No | | no-deprecated-error-params | Disallow invalid_type_error/required_error. Use error param. | ✅ Yes | | no-deprecated-format-methods | Disallow .format()/.flatten() on ZodError. Use z.treeifyError(). | ❌ No | | no-merge-method | Disallow .merge(). Use .extend() instead. | ❌ No | | no-superrefine | Disallow .superRefine(). Use .check() instead. | ✅ Yes | | no-errors-property | Disallow error.errors. Use error.issues instead. | ✅ Yes | | no-deprecated-object-methods | Disallow .strict()/.passthrough()/.strip(). Use top-level functions. | ❌ No | | no-native-enum | Disallow z.nativeEnum(). Use z.enum() instead. | ❌ No | | no-deep-partial | Disallow .deepPartial() (removed in v4). | ❌ No | | no-deprecated-ip-methods | Disallow .ip()/.cidr(). Use .ipv4()/.ipv6() variants. | ❌ No | | no-promise-schema | Disallow z.promise(). Await before parsing. | ❌ No | | no-throw-in-refine | Disallow throw inside .refine()/.superRefine()/.transform(). Use return patterns. | ❌ No | | require-enum-as-const | Require as const for arrays passed to z.enum(). | ❌ No |

Best Practices (severity: warn)

These rules enforce Zod v4 best practices for optimal code quality.

| Rule | Description | Fixable | |------|-------------|---------| | prefer-safeParse | Prefer .safeParse() over .parse() for explicit error handling. | ✅ Yes | | no-schema-in-render | Disallow creating schemas inside functions/components. React-aware: allows useMemo/useCallback. | ❌ No | | prefer-error-param | Prefer error param over deprecated message param. | ✅ Yes |

React Integration

Schema Creation in Components

The no-schema-in-render rule is React-aware and recognizes memoization patterns:

// ❌ Bad - Schema recreated every render
const MyComponent = () => {
  const schema = z.object({ name: z.string() })  // Error!
  return <Form schema={schema} />
}

// ✅ Good - Schema at module level
const schema = z.object({ name: z.string() })
const MyComponent = () => {
  return <Form schema={schema} />
}

// ✅ Good - Schema memoized with useMemo (for translated schemas)
const MyComponent = () => {
  const t = useTranslations()
  const schema = useMemo(() => z.object({
    email: z.email(t('invalidEmail')),
  }), [t])
  return <Form schema={schema} />
}

// ✅ Good - Factory function with useMemo
const createSchema = (t) => z.object({ email: z.email(t('error')) })
const MyComponent = () => {
  const t = useTranslations()
  const schema = useMemo(() => createSchema(t), [t])
  return <Form schema={schema} />
}

TypeScript Workaround for .check() ctx.addIssue

When using .check(), TypeScript may incorrectly flag ctx.addIssue() calls:

// If you see: @typescript-eslint/no-unsafe-call on ctx.addIssue()
// Add this comment to suppress the false positive:
.check((ctx) => {
  const { value: data } = ctx;
  if (!data.name) {
    // eslint-disable-next-line @typescript-eslint/no-unsafe-call -- Zod v4 ctx.addIssue is type-safe at runtime
    ctx.addIssue({ code: 'custom', message: 'Name required' });
  }
})

Migration Guide

From Zod v3 to v4

1. String Format Methods

// Before (deprecated)
z.string().email()
z.string().url()
z.string().uuid()

// After (v4)
z.email()
z.url()
z.uuid()

2. Record Schema

// Before (v3 - single argument)
z.record(z.string())

// After (v4 - two arguments)
z.record(z.string(), z.string())

3. Error Parameters

// Before (deprecated)
z.string({ invalid_type_error: "Must be string", required_error: "Required" })
z.string().refine(fn, { message: "Error" })

// After (v4)
z.string({ error: "Must be string" })
z.string().refine(fn, { error: "Error" })
// Or with function
z.string({ error: (iss) => `Error: ${iss.code}` })

4. ZodError Methods

// Before (deprecated)
error.format()
error.flatten()

// After (v4)
z.treeifyError(error)

5. ZodError Property

// Before (v3)
error.errors

// After (v4)
error.issues

6. Schema Merging

// Before (deprecated)
schema1.merge(schema2)

// After (v4)
schema1.extend(schema2.shape)
// Or
z.object({ ...schema1.shape, ...schema2.shape })

7. Super Refine → Check

// Before (deprecated)
schema.superRefine((val, ctx) => {
  if (!isValid(val)) {
    ctx.addIssue({ code: "custom", message: "Invalid" })
  }
})

// After (v4)
schema.check((ctx) => {
  const { value: val } = ctx;
  if (!isValid(val)) {
    ctx.addIssue({ code: "custom", message: "Invalid" })
  }
})

// Or for simple cases
schema.check((val) => isValid(val) || "Invalid")

8. Object Methods

// Before (deprecated)
z.object({ name: z.string() }).strict()
z.object({ name: z.string() }).passthrough()

// After (v4)
z.strictObject({ name: z.string() })
z.looseObject({ name: z.string() })

9. Native Enums

// Before (deprecated)
z.nativeEnum(MyEnum)

// After (v4)
z.enum(MyEnum)  // z.enum() now supports native enums

10. IP and CIDR Validation

// Before (removed)
z.string().ip()
z.string().cidr()

// After (v4)
z.ipv4()  // or z.ipv6()
z.cidrv4()  // or z.cidrv6()
z.union([z.ipv4(), z.ipv6()])  // for both

11. Promise Schema

// Before (deprecated)
z.promise(z.string())

// After (v4)
const data = await fetchData()
schema.parse(data)  // await before parsing

12. Deep Partial

// Before (removed)
schema.deepPartial()

// After (v4)
schema.partial()  // shallow only
// For deep partial, manually create nested partial schemas

Changelog

v0.4.0 (2026-06-21)

ESLint 10 Compatibility:

  • Peer dependency updated to support both ESLint 9.x and 10.x ("eslint": "^9.0.0 || ^10.0.0").
  • Minimum Node.js bumped to 20.19.0 to match ESLint 10's engine requirements.
  • Plugin meta now exposes namespace: "zod-v4" so the plugin resolves correctly under flat config (--cache, --print-config).
  • Plugin meta.version is now in sync with the published package version.
  • eslint.config.js rewritten to use parserOptions.projectService (recommended by @typescript-eslint v8) so tests and config files lint cleanly without polluting the type-aware scope.
  • Resolved 10 transitive vulnerabilities (npm audit fix) in dev dependencies.

Notes:

  • No rule code changes required — the plugin already used the modern flat config APIs and does not rely on any context/SourceCode methods removed in ESLint 10.
  • Fixes a pre-existing TypeScript inference bug in src/utils/zod-helpers.ts (findRootZodCall) that surfaced under strict + noUncheckedIndexedAccess.

v0.3.0 (2025-02-21)

New Rules:

  • no-throw-in-refine: Detects throw statements inside .refine(), .superRefine(), and .transform() callbacks. These errors are not captured by Zod, causing silent failures. The rule provides educational messages with correct patterns for each method.
  • require-enum-as-const: Requires as const for arrays passed to z.enum(). Without it, TypeScript infers string[] instead of the literal union, breaking type inference.

Improvements:

  • Added 61 new test cases (276 total tests passing)
  • Updated documentation with migration examples for new rules

v0.2.0 (2025-12-16)

New Features:

  • no-superrefine: Now has auto-fix support! Transforms .superRefine((data, ctx) => {...}) to .check((ctx) => { const { value: data } = ctx; ...})
  • no-schema-in-render: Now recognizes useMemo and useCallback - schemas inside memoized callbacks are allowed
  • Improved error messages with migration examples

Bug Fixes:

  • Fixed false positives in no-schema-in-render when using React memoization hooks

v0.1.1 (2025-12-15)

  • Initial release with 15 rules (12 breaking changes + 3 best practices)

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting a PR.

License

MIT License - see LICENSE for details.

Author

Matheus Pimenta - Koda AI Studio

Built with Claude Code.