Skip to content
Nilay Kabariya

Entry No.056·Fixes··4 min read

ESLint "couldn't find an eslint.config.* file" (ESLint 10)

ESLint 10 dropped .eslintrc for good, and the old escape hatch is gone. Reproduced on 10.12.0, plus the React and Next.js plugin errors that follow.

by Nilay#eslint#node#npmFIXED

The error, verbatim

Jump to the fix ↓
Oops! Something went wrong! :(

ESLint: 10.12.0

ESLint couldn't find an eslint.config.* file.

From ESLint v9.0.0, the default configuration file is now eslint.config.*.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

Tested on

eslint
10.12.0 and 9.39.5
@eslint/js / globals
10.0.1 / 14.0.0
eslint-config-next
16.4.0 (with next 16.4.0)
Node / npm / OS
24.14.0 / 11.9.0 / Windows 11 Pro

✓ Re-verified on eslint v10.14.0, eslint-plugin-react 7.37.5 (Linux): 3/3 checks still hold · results

Contents
  1. The fix01
  2. “‘console’ is not defined” after migrating02
  3. ESLint 10 with React and Next.js plugins03
  4. “‘React’ must be in scope when using JSX”04
  5. What didn’t work05
  6. How this was tested06

This error means ESLint looked for a config file in the new format and found none. Usually there is a config: an .eslintrc.json, .eslintrc.js or .eslintrc.yml that worked for years. ESLint 9 made eslint.config.* the default, and ESLint 10 removed support for .eslintrc completely, so that file is now simply ignored.

I reproduced it with a project that had only an .eslintrc.json, on both versions:

ESLint 9.39.5 ESLint 10.12.0
Message couldn't find an eslint.config.(js|mjs|cjs) file couldn't find an eslint.config.* file
ESLINT_USE_FLAT_CONFIG=false Still loads .eslintrc, with a deprecation warning Ignored: same error

ESLint 10 also exits with code 2, so CI fails on this step. There’s no switch that brings the old format back. The config has to move.

The fix

The quickest way is ESLint’s own converter, run in the folder with your old config:

npx @eslint/migrate-config .eslintrc.json

It writes eslint.config.mjs and prints the packages the new file imports. Install exactly those. For my config it asked for:

npm install -D @eslint/js @eslint/eslintrc

The converted file wraps your old settings in a compatibility layer (FlatCompat). That’s fine, but for a small config a hand-written file is shorter and has no compatibility package:

import js from "@eslint/js";
import globals from "globals";
import { defineConfig } from "eslint/config";

export default defineConfig([
  js.configs.recommended,
  { languageOptions: { globals: globals.node } },
]);

“‘console’ is not defined” after migrating

The next surprise is a flood of no-undef errors for things that obviously exist:

2:1  error  'console' is not defined  no-undef

.eslintrc had env: { node: true } or env: { browser: true } to declare those globals. Flat config has no env.

The globals come from the globals package now. The converter handles it when the old file had an env: it turned node: true into ...globals.node and asked me to install globals. A hand-written config needs it explicitly, which is the languageOptions.globals line in the config above. Use globals.browser for front-end code.

ESLint 10 with React and Next.js plugins

This is the part behind most “eslint 10” searches. Several of the most-used plugins don’t declare support for ESLint 10 yet. Their peerDependencies on October 10, 2026:

Plugin Version Declares ESLint 10?
eslint-plugin-react 7.37.5 ❌ up to ^9.7
eslint-plugin-import 2.32.0 ❌ up to ^9
eslint-plugin-jsx-a11y 6.10.2 ❌ up to ^9
eslint-plugin-react-hooks 7.1.1 ✅
typescript-eslint 8.71.1 ✅
eslint-plugin-vue 10.11.1 ✅
eslint-config-next 16.4.0 ✅ >=9.0.0, but it depends on the first three

How that showed up depended on how the plugin got into the project. Installing eslint-plugin-react directly next to ESLint 10 failed outright, and nothing was installed:

npm error code ERESOLVE
npm error Found: eslint@10.12.0
npm error Could not resolve dependency:
npm error peer eslint@"^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" from eslint-plugin-react@7.37.5

Through eslint-config-next, npm only warned (npm warn ERESOLVE overriding peer dependency, three times) and installed everything.

In both cases the plugins worked on ESLint 10. With eslint-config-next’s core-web-vitals config, a test file got errors and warnings from react/jsx-key, react-hooks/rules-of-hooks, jsx-a11y/alt-text and @next/next/no-img-element, so all of those plugins loaded and ran. The block is the declared version range, not the code.

To install a plugin that hasn’t declared ESLint 10 yet, tell npm to accept your ESLint for it, in package.json:

{
  "overrides": {
    "eslint-plugin-react": { "eslint": "$eslint" }
  }
}

npm install --legacy-peer-deps also worked, but it has to be repeated on every install and in CI. The override stays in the project. Remove it when the plugin publishes ESLint 10 support.

“‘React’ must be in scope when using JSX”

If you set up eslint-plugin-react by hand with its recommended config, every JSX file gets this:

2:10  error  'React' must be in scope when using JSX  react/react-in-jsx-scope

The recommended config still assumes the old JSX transform from before React 17.

Add the plugin’s jsx-runtime config after the recommended one:

import react from "eslint-plugin-react";
import { defineConfig } from "eslint/config";

export default defineConfig([
  {
    files: ["**/*.jsx"],
    ...react.configs.flat.recommended,
    ...react.configs.flat["jsx-runtime"],
    rules: { ...react.configs.flat.recommended.rules, ...react.configs.flat["jsx-runtime"].rules },
    settings: { react: { version: "19.0" } },
  },
]);

One more thing if you’re on Next.js: create-next-app 16.4 with its defaults no longer adds ESLint at all, so there’s no lint script or config to migrate. You add both yourself.

What didn’t work

How this was tested

Fresh npm projects on Windows 11 with Node 24.14.0 and npm 11.9.0:

  • an .eslintrc.json-only project on ESLint 10.12.0 and 9.39.5, with and without ESLINT_USE_FLAT_CONFIG=false
  • @eslint/migrate-config on a config without env and one with env: { node: true }, run before and after installing the packages it asked for, and a hand-written flat config
  • eslint 10.12.0 with eslint-config-next 16.4.0 and next 16.4.0, linting a JSX file written to break rules from each plugin
  • eslint 10.12.0 with eslint-plugin-react 7.37.5 installed directly, then with --legacy-peer-deps, then with an overrides entry

I only tested the rules those configs enable. A plugin that hasn’t declared ESLint 10 support could still have a rule that breaks on it.

The code is public, so you can run it yourself: eslint10 in nk-repro.

— N.K., end of entry No.056

Useful? Pass it on:Post on XFollow @EmotionalMatter

Related entries

  1. No.021

    Fix: typescript-eslint does not support TS 7.0 (ESLint on TS 7)

    When typescript-eslint will support TypeScript 7, why npm refuses the install, and the setup that keeps TypeScript 7 while ESLint runs normally today.

    > npm error code ERESOLVE

    FIXED4 min
  2. No.093

    Fix: bigint Failed to load bindings, pure JS will be used

    bigint: Failed to load bindings, pure JS will be used. Why npm 12 shows it on every install, whether it matters, and how to get the native build back.

    > bigint: Failed to load bindings, pure JS will be used (try npm run rebuild?)

    FIXED3 minWindows
  3. No.091

    Fix: npm error code EALLOWREMOTE (and EALLOWGIT), tested

    npm error code EALLOWREMOTE: Fetching packages of type remote have been disabled. Every case reproduced on npm 12, from npx to npm ci, with each fix.

    > npm error code EALLOWREMOTE

    FIXED4 min
  4. No.086

    npm error code EACCES: permission denied, 3 causes tested

    npm error code EACCES with syscall mkdir or open: a global install, a cache that sudo left behind, or Docker. Each reproduced, with the fix that lasts.

    > npm error code EACCES

    FIXED2 min

Post card · Every Sunday

What broke this week.

One email on Sundays: the Node, TypeScript, Python and AI SDK releases that broke builds that week, each one reproduced, with a link to the fix that worked. Never shared, and you can leave any time.

— Nilay

or follow by RSSor on X

By subscribing you agree to the privacy note. One click to leave.

tip: paste the exact error text