| | |

ESLint 27 🛡️ ESLint v10 Breaking Changes — Node version & rule updates

⏱️ Estimated read: 10 min | |

ESLint v10.0.0 was released on February 5, 2026, and it is the release that completes the flat config migration that began in v9. The legacy .eslintrc system is gone entirely, the Node.js support window has moved forward, and several core rules have changed their defaults or their schemas in ways that will surface new lint reports in existing codebases . The migration is not a version bump that can be deferred; v10 no longer reads the files that v9 could still read through a compatibility layer.

The Node.js requirement is the first gate. ESLint v10 supports Node.js ^20.19.0 || ^22.13.0 || >=24. The v21 and v23 lines are dropped entirely, and anything below v20.19.0 is unsupported . A project on Node 18 or Node 21 must upgrade the runtime before it can upgrade ESLint, and an editor integration that bundles its own Node.js must be checked separately.

Key point: ESLint v10 removes the legacy eslintrc system, requires Node.js ^20.19.0 || ^22.13.0 || >=24, changes the config lookup to start from each linted file’s directory, updates eslint:recommended, and changes several core rule defaults and schemas. The eslint-plugin-import package is the main ecosystem blocker; eslint-plugin-import-x is the drop-in replacement.


Why the Node version and the rule updates matter together

The runtime gate problem. The Node.js requirement is a hard block. ESLint v10 cannot be installed on an unsupported runtime, and the error is an engine mismatch rather than a configuration error. The migration guide lists the supported versions explicitly, and the release notes confirm the engines field was tightened to ^20.19.0 || ^22.13.0 || >=24 . A CI pipeline that runs on an older Node image will fail at npm install before the lint step even runs.

The eslint:recommended update. The recommended config in v10 includes three new rules that were not in the v9 config. The migration guide states that the config “is updated to include new rules that we feel are important” . A project that extends js.configs.recommended will see new lint reports after the upgrade, and the reports are not a bug; they are the updated config doing what it was designed to do. The fix is to address the reports or disable the new rules explicitly.

The rule schema and default changes. Several core rules have stricter schemas or changed defaults. The radix rule’s "always" and "as-needed" string options are deprecated, and the rule now always enforces providing a radix . The no-shadow-restricted-names rule now treats globalThis as a restricted name by default, so const globalThis = "foo" is reported . The func-names schema disallows extra array items, and the no-invalid-regexp allowConstructorFlags option no longer accepts duplicate flags . Each change is small, and each one can break a configuration that was valid in v9.

The trade-off. The v10 release is a cleanup release. It removes the compatibility layers that made the v9 transition gradual, and it tightens the rules that were too permissive. A project that did the v9 flat config migration has a shorter path to v10; a project that is still on .eslintrc has to do both migrations before it can run v10. The Node.js requirement is the constraint that determines when the migration can start.


a. The Node version and the config lookup change

The Node.js requirement is the first check. The engines field in package.json and the CI image must both be on a supported version.

// package.json — the Node.js requirement
{
  "engines": {
    "node": "^20.19.0 || ^22.13.0 || >=24"
  }
}

The config lookup change is the second structural change. In v9, ESLint looked for eslint.config.* starting from the current working directory. In v10, it looks from the directory of each linted file, walking upward to the filesystem root . This is the behavior that was behind the v10_config_lookup_from_file feature flag in v9, and the flag is now removed. A monorepo with per-package configs benefits from the change, because each file’s config is resolved from its own location. A project that relied on the cwd-based lookup can pass an explicit --config path.

# The explicit config path
npx eslint . --config path/to/eslint.config.js

The eslint-env comments are another removal. In v9, the comments were deprecated; in v10, they are reported as errors . A codebase that still uses /* eslint-env node */ must replace the comment with the equivalent languageOptions.globals configuration.


b. The rule updates and the ecosystem blocker

The rule updates are the changes that produce new lint reports. The radix rule is the simplest case: the rule now always requires the radix argument, and the "always" and "as-needed" options are deprecated . A config that sets radix: ["error", "as-needed"] must remove the option and update the code, or disable the rule.

// The radix rule change
// v9: radix: ["error", "as-needed"] allowed parseInt("10")
// v10: the rule always requires parseInt("10", 10)

The no-shadow-restricted-names change is the one that produces new reports without any config change. The rule now treats globalThis as a restricted name by default, and the reportGlobalThis option defaults to true . A local variable named globalThis is now reported, and the fix is to rename the variable or to set reportGlobalThis: false.

// The no-shadow-restricted-names change
// v10 reports this by default:
const globalThis = "foo";

The ecosystem blocker is eslint-plugin-import. The latest release (2.32.0) does not declare ESLint 10 in its peer range, and it crashes at plugin-load time on v10 with a TypeError . The community replacement is eslint-plugin-import-x, a drop-in fork with an import-x/ rule prefix and a peer range that includes ^10.0.0 . A project that uses eslint-plugin-import must either wait for a v10-compatible release or swap to the fork.


Complete Example Session

// ============================================
// PART 1: THE NODE VERSION CHECK
// ============================================

// The supported Node.js versions:
// ^20.19.0 || ^22.13.0 || >=24

// package.json
{
  "engines": {
    "node": "^20.19.0 || ^22.13.0 || >=24"
  }
}

// ============================================
// PART 2: THE CONFIG LOOKUP CHANGE
// ============================================

// v10 looks for eslint.config.* starting from
// the directory of each linted file.

// A monorepo with per-package configs works.
// A project that relied on the cwd lookup
// can pass an explicit --config path.

// ============================================
// PART 3: THE ESLINT:RECOMMENDED UPDATE
// ============================================

// The recommended config includes new rules.
// A project that extends it will see new reports.

// import js from "@eslint/js";
// export default [js.configs.recommended];

// ============================================
// PART 4: THE RADIX RULE
// ============================================

// v9: radix: ["error", "as-needed"] allowed parseInt("10")
// v10: the rule always requires parseInt("10", 10)
// Remove the option and update the code.

// ============================================
// PART 5: THE NO-SHADOW-RESTRICTED-NAMES RULE
// ============================================

// v10 reports globalThis by default.
// const globalThis = "foo"; // reported

// The fix: rename the variable or set the option.
// "no-shadow-restricted-names": ["error", { "reportGlobalThis": false }]

// ============================================
// PART 6: THE FUNC-NAMES SCHEMA
// ============================================

// v10 disallows extra array items.
// ["error", "always", { "generators": "never" }, "foo"] // invalid

// Remove the extra item.

// ============================================
// PART 7: THE NO-INVALID-REGEXP SCHEMA
// ============================================

// v10 disallows duplicate flags.
// ["u", "y", "u"] // invalid

// Remove the duplicate.

// ============================================
// PART 8: THE ESLINT-ENV COMMENTS
// ============================================

// v10 reports eslint-env comments as errors.
// /* eslint-env node */ // reported

// Replace with languageOptions.globals.

// ============================================
// PART 9: THE ECOSYSTEM BLOCKER
// ============================================

// eslint-plugin-import 2.32.0 crashes on v10.
// The replacement is eslint-plugin-import-x.

// npm install -D eslint-plugin-import-x

// The rule names change from import/ to import-x/.

// ============================================
// PART 10: THE CHECKLIST
// ============================================

// [ ] The Node.js version is ^20.19.0 || ^22.13.0 || >=24.
// [ ] The eslint.config.* lookup starts from each file's directory.
// [ ] The eslint:recommended reports are addressed.
// [ ] The radix rule options are removed.
// [ ] The globalThis shadowing is renamed or the option is set.
// [ ] The func-names schema is cleaned up.
// [ ] The no-invalid-regexp flags are unique.
// [ ] The eslint-env comments are replaced with globals.
// [ ] The eslint-plugin-import is replaced with import-x.

The ten parts covered the Node version, the config lookup, the recommended config, the radix rule, the shadow-restricted-names rule, the func-names schema, the no-invalid-regexp schema, the eslint-env comments, the ecosystem blocker, and the checklist. The through-line is that v10 is a cleanup release, and the cleanup produces new reports and new constraints.


Quick Reference

The Node.js requirement

VersionStatus
v20.19.0+Supported
v22.13.0+Supported
v24+Supported
v21, v23Dropped
< v20.19.0Dropped

The rule changes

RuleChange
radix"always" and "as-needed" deprecated; radix always required
no-shadow-restricted-namesglobalThis reported by default
func-namesExtra array items invalid
no-invalid-regexpDuplicate allowConstructorFlags invalid

The config changes

ChangeDetail
eslintrc removal.eslintrc.* and .eslintignore no longer honored
Config lookupStarts from each file’s directory
eslint-env commentsReported as errors

The ecosystem

PackageStatus
eslint-plugin-importCrashes on v10
eslint-plugin-import-xv10-compatible

Best Practices

✅ Do This:

// Set the Node.js engines field
"engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }            // ✅
// Replace eslint-env with globals
languageOptions: { globals: { ...globals.node } }                // ✅
// Swap to eslint-plugin-import-x
npm install -D eslint-plugin-import-x                            // ✅

❌ Don’t Do This:

// Keep the radix "as-needed" option
radix: ["error", "as-needed"] // deprecated in v10               // ❌
// Shadow globalThis without setting the option
const globalThis = "foo"; // reported by default                 // ❌

Common Pitfalls

PitfallWhy It HappensFix
Install failsNode.js is below v20.19.0Upgrade the runtime
New lint reportseslint:recommended updatedAddress or disable the new rules
The plugin crasheseslint-plugin-import on v10Swap to eslint-plugin-import-x
eslint-env errorsThe comments are reportedReplace with languageOptions.globals
Config not foundThe lookup changedCheck the file’s directory or use --config

Real-World Examples

1. The Node engines

{ "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }

2. The config lookup

npx eslint . --config ./eslint.config.js

3. The radix rule

radix: "error" // no options

4. The globalThis option

"no-shadow-restricted-names": ["error", { "reportGlobalThis": false }]

5. The func-names schema

"func-names": ["error", "always", { "generators": "never" }]

6. The no-invalid-regexp flags

"no-invalid-regexp": ["error", { "allowConstructorFlags": ["u", "y"] }]

7. The eslint-env replacement

languageOptions: { globals: { ...globals.node } }

8. The import-x swap

npm install -D eslint-plugin-import-x

9. The rule prefix

"import-x/no-unresolved": "error"

10. The codemod

npx codemod @eslint/v9-to-v10

Visual

The v10 Breaking Changes

┌──────────────────────────────────────────────────────────────┐
│  THE FOUR CATEGORIES                                          │
│                                                              │
│  Runtime                                                     │
│  ┌─────────────────────┐                                    │
│  │ Node.js 20.19+      │  The hard gate                     │
│  │ v21, v23 dropped    │                                    │
│  └─────────────────────┘                                    │
│                                                              │
│  Config                                                      │
│  ┌─────────────────────┐                                    │
│  │ eslintrc removed    │                                    │
│  │ Lookup from file    │                                    │
│  └─────────────────────┘                                    │
│                                                              │
│  Rules                                                       │
│  ┌─────────────────────┐                                    │
│  │ radix               │                                    │
│  │ globalThis          │                                    │
│  │ func-names          │                                    │
│  └─────────────────────┘                                    │
│                                                              │
│  Ecosystem                                                   │
│  ┌─────────────────────┐                                    │
│  │ import → import-x   │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

The Config Lookup

┌──────────────────────────────────────────────────────────────┐
│  v9 VS v10 LOOKUP                                             │
│                                                              │
│  v9:                                                         │
│  CWD → eslint.config.js                                      │
│  The same config for every file.                             │
│                                                              │
│  v10:                                                        │
│  File directory → walk up → eslint.config.js                 │
│  Each file resolves its own config.                          │
│                                                              │
│  The monorepo per-package config works without flags.        │
└──────────────────────────────────────────────────────────────┘

The Rule Defaults

┌──────────────────────────────────────────────────────────────┐
│  THE NEW REPORTS                                              │
│                                                              │
│  const globalThis = "foo";                                   │
│  ┌─────────────────────┐                                    │
│  │ v9: not reported    │                                    │
│  │ v10: reported       │  reportGlobalThis: true            │
│  └─────────────────────┘                                    │
│                                                              │
│  parseInt("10")                                              │
│  ┌─────────────────────┐                                    │
│  │ v9: allowed with    │                                    │
│  │   "as-needed"       │                                    │
│  │ v10: always         │  radix required                    │
│  │   requires radix    │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
Release dateFebruary 5, 2026
Node.js requirement`^20.19.0
eslintrc systemRemoved
Config lookupFrom each file’s directory
eslint:recommendedUpdated with new rules
radix rule"always" and "as-needed" deprecated
no-shadow-restricted-namesglobalThis reported by default
Ecosystem blockereslint-plugin-import → eslint-plugin-import-x

Key takeaways:

  • The Node.js requirement is the first gate. ESLint v10 supports ^20.19.0 || ^22.13.0 || >=24. The v21 and v23 lines are dropped, and anything below v20.19.0 is unsupported. A project must upgrade the runtime before it can install v10 .
  • The eslintrc system is gone. The .eslintrc.* files, the .eslintignore files, and the eslint-env comments are no longer honored. The flat config is the only configuration format, and the eslint-env comments are reported as errors .
  • The config lookup starts from each file’s directory. The cwd-based lookup is replaced by a file-based lookup, which is the behavior that was behind the v10_config_lookup_from_file flag in v9. A monorepo with per-package configs benefits; a project that relied on the cwd lookup can pass an explicit --config path .
  • The eslint:recommended config and several core rules changed. The recommended config includes new rules, the radix rule always requires a radix, the no-shadow-restricted-names rule reports globalThis by default, and the func-names and no-invalid-regexp schemas are stricter .
  • eslint-plugin-import is the main ecosystem blocker. The latest release does not support v10 and crashes at plugin-load time. The drop-in replacement is eslint-plugin-import-x, which uses the import-x/ rule prefix and declares v10 support .

Remember: ESLint v10 is the release that completes the flat config migration. The Node.js requirement is the hard gate, the eslintrc removal is the structural change, and the rule updates are the source of the new lint reports. The eslint-plugin-import package is the ecosystem blocker, and the import-x fork is the replacement. The migration is a cleanup, and the cleanup is the release.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!