eslint-plugin-zod
ESLint plugin that adds custom linting rules to enforce best practices when using Zod.
It can also work with Oxlint!
Find out more about Oxlint's jsPlugins.
Rules
Configurations enabled in.
Set in the recommended configuration.
Set in the strict configuration.
Set in the stylistic configuration.
Automatically fixable by the --fix CLI option.
Manually fixable by editor suggestions.
| Name | Description | |||
|---|---|---|---|---|
| array-style | Enforce consistent Zod array style | |||
| consistent-import | Enforce a consistent import style for Zod | |||
| consistent-import-source | Enforce consistent source from Zod imports | |||
| consistent-object-schema-type | Enforce consistent usage of Zod schema methods | |||
| consistent-schema-output-type-style | Enforce consistent use of z.infer or z.output for schema type inference | |||
| consistent-schema-var-name | Enforce a consistent naming convention for Zod schema variables | |||
| no-any-schema | Disallow usage of z.any() in Zod schemas |
|||
| no-coerce-boolean | Disallow z.coerce.boolean() because it treats any non-empty string as true. |
|||
| no-conflicting-checks | Disallow check combinations that can never match, are redundant, or do not apply to the schema type | |||
| no-duplicate-schema-methods | Disallow calling the same schema method more than once in a single chain | |||
| no-dynamic-schema-value | Disallow non-static values passed as arguments in a Zod schema expression | |||
| no-empty-custom-schema | Disallow usage of z.custom() without arguments |
|||
| no-function-scoped-schema | Disallow constructing a Zod schema inside a function body | |||
| no-native-enum | Disallow deprecated z.nativeEnum() in favor of z.enum(). |
|||
| no-number-schema-with-finite | Disallow deprecated z.number().finite(). In Zod 4+ number schemas do not allow infinite values by default, so it is a no-op. |
|||
| no-number-schema-with-int | Disallow usage of z.number().int() as it is considered legacy |
|||
| no-number-schema-with-is-finite | Disallow using deprecated isFinite on a Zod number schema; in v4+ it is always true. |
|||
| no-number-schema-with-is-int | Disallow using deprecated isInt on a Zod number schema; check the format property instead. |
|||
| no-number-schema-with-safe | Disallow deprecated z.number().safe(). Use z.int(); .safe() is now identical to .int(). |
|||
| no-number-schema-with-step | Disallow deprecated z.number().step(). Use .multipleOf() instead. |
|||
| no-optional-and-default-together | Disallow using both .optional() and .default() on the same Zod schema |
|||
| no-promise-schema | Disallow deprecated z.promise() schemas. |
|||
| no-schema-with-is-nullable | Disallow deprecated .isNullable() on a Zod schema; use safeParse(null).success instead. |
|||
| no-schema-with-is-optional | Disallow deprecated .isOptional() on a Zod schema; use safeParse(undefined).success instead. |
|||
| no-throw-in-refine | Disallow throwing errors directly inside Zod refine callbacks | |||
| no-transform-in-record-key | Disallow transforms in z.record() key schemas, which can cause silent key mutations and data loss through key collisions | |||
| no-unknown-schema | Disallow usage of z.unknown() in Zod schemas |
|||
| no-unnecessary-readonly | Disallow .readonly() on schemas whose output is already immutable |
|||
| prefer-enum-over-literal-union | Prefer z.enum() over z.union() when all members are string literals. |
|||
| prefer-loose-object | Prefer z.looseObject() over z.object().passthrough() and z.object().loose() |
|||
| prefer-map-set-size-over-min-max | Prefer .size(n) over .min(n).max(n) with the same value on a set or map schema |
|||
| prefer-meta | Enforce usage of .meta() over .describe() |
|||
| prefer-meta-last | Enforce .meta() as last method |
|||
| prefer-nullish | Enforce .nullish() instead of combining .optional() and .nullable() |
|||
| prefer-strict-object | Prefer z.strictObject() over z.object().strict() |
|||
| prefer-string-length-over-min-max | Prefer .length(n) over .min(n).max(n) with the same value on a string schema |
|||
| prefer-string-schema-with-trim | Enforce z.string().trim() to prevent accidental leading/trailing whitespace |
|||
| prefer-top-level-string-formats | Prefer top-level string format schemas over deprecated z.string().<format>() methods |
|||
| prefer-trim-before-string-length-checks | Enforce .trim() is called before string length checks to ensure accurate validation |
|||
| prefer-tuple-over-array-length | Prefer z.tuple() over a length-constrained z.array() so the length is preserved in the inferred type. |
|||
| prefer-validate | Prefer boolean validation when only the success of parsing is used | |||
| require-brand-type-parameter | Require type parameter on .brand() functions |
|||
| require-error-message | Enforce that custom refinements include an error message | |||
| schema-error-property-style | Enforce consistent style for error messages in Zod schema validation (using ESQuery patterns) |
Installation
ESLint
Install eslint and eslint-plugin-zod using your preferred package manager:
npm i --save-dev eslint eslint-plugin-zod
yarn add --dev eslint eslint-plugin-zod
pnpm add --save-dev eslint eslint-plugin-zod
ESLint Configuration
Import the plugin
import eslintPluginZod from 'eslint-plugin-zod';Add
recommendedconfig to your ESLint setupeslintPluginZod.configs.recommended,
Here's a minimal example using the flat config format:
// eslint.config.js
import { defineConfig } from 'eslint/config';
import eslint from '@eslint/js';
import eslintPluginZod from 'eslint-plugin-zod';
export default defineConfig(eslint.configs.recommended, eslintPluginZod.configs.recommended);
Oxlint
Install oxlint and eslint-plugin-zod using your preferred package manager:
npm i --save-dev oxlint eslint-plugin-zod
yarn add --dev oxlint eslint-plugin-zod
pnpm add --save-dev oxlint eslint-plugin-zod
Oxlint Configuration
Import the plugin
import eslintPluginZod from 'eslint-plugin-zod';Add
eslint-plugin-zodto thejsPluginskey{ jsPlugins: ['eslint-plugin-zod'], // ... }Add
eslintPluginZod.configs.recommended.rulesto your Oxlint config.
Alternatively you can specify the rules manually
Here's a minimal example using the flat config format:
// oxlint.config.ts
import eslintPluginZod from 'eslint-plugin-zod';
import { defineConfig } from 'oxlint';
export default defineConfig({
jsPlugins: ['eslint-plugin-zod'],
rules: {
...eslintPluginZod.configs.recommended.rules,
},
});
Configurations
| Config | Contents |
|---|---|
recommended |
Rules catching bugs and deprecated APIs |
strict |
recommended plus stricter rules; use it in place of recommended |
stylistic |
Style and consistency rules; layer it on top of either of the above |
all |
Every non-deprecated rule; meant for discovery rather than daily use |
strict already includes recommended, so enable one of the two, then add stylistic if you want it:
export default defineConfig(
eslint.configs.recommended,
eslintPluginZod.configs.strict,
eslintPluginZod.configs.stylistic,
);
The same configs work in Oxlint through their rules key.
Zod peer dependency version
eslint-plugin-zod is designed for projects that use zod@^4.
While the plugin analyzes Zod schemas in your code,
it doesn't import or depend on Zod at runtime.
To document this relationship without forcing installation,
Zod is declared as an optional peer dependency in the plugin's package.json.
If your project uses Zod v4, the plugin will automatically lint your schemas. If you're not using Zod (for example, in a separate ESLint workspace), you don't need to install it.
eslint and oxlint are optional peer dependencies too: a project runs one of the two linters, so the plugin cannot require either.