Fix: ESLint Config Not Working — Rules Ignored, Flat Config Errors, or Plugin Not Found
Quick Answer
How to fix ESLint configuration issues — flat config vs legacy config, extends conflicts, parser options, plugin resolution, per-directory overrides, and migrating to ESLint 9.
The Problem
ESLint rules are defined in the config but have no effect:
// .eslintrc.json
{
"rules": {
"no-console": "error"
}
}
// Running eslint src/ — console.log() passes without errorOr ESLint 9 throws a config error:
Error: Key "extends": Key 0: string is not supported in flat config.
You're using a eslintrc config which is not supported in ESLint v9.Or a plugin can’t be found despite being installed:
Error: Failed to load plugin 'react' declared in '.eslintrc': Cannot find module 'eslint-plugin-react'Or TypeScript-specific rules don’t apply to .ts files.
Why This Happens
ESLint has two incompatible config systems that are easy to mix up:
- ESLint 9 uses flat config by default —
eslint.config.js(or.mjs/.cjs). The oldextends,.eslintrc.*format, andeslintignoreare no longer supported without a compatibility layer. - ESLint 8 and below use legacy config —
.eslintrc.json,.eslintrc.js,.eslintrc.yaml.extendsis an array of strings. This format is deprecated. - Plugin resolution changed in flat config — in legacy config, plugins are referenced as strings (
"plugin:react/recommended"). In flat config, you import them as JavaScript modules. - File matching — rules in
overridesor flat configfilesglob patterns must match your file paths exactly. A pattern that doesn’t match silently skips the file.
Fix 1: Determine Which Config System You’re Using
# Check your ESLint version
npx eslint --version
# v8.x.x → legacy config (.eslintrc.*)
# v9.x.x → flat config (eslint.config.js) by default
# Check which config file ESLint found
npx eslint --print-config src/index.js
# Shows the resolved config for that file
# If it shows empty rules, the config isn't being applied
# Lint a specific file with debug output
npx eslint --debug src/index.js 2>&1 | grep -E "config|plugin"ESLint 9 with legacy config compatibility:
// eslint.config.js — use FlatCompat to use old configs in ESLint 9
import { FlatCompat } from '@eslint/eslintrc';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const compat = new FlatCompat({ baseDirectory: __dirname });
export default [
// Convert legacy configs
...compat.extends('eslint:recommended'),
...compat.extends('plugin:react/recommended'),
// Add flat config rules on top
{
rules: {
'no-console': 'warn',
},
},
];Fix 2: Write a Correct ESLint 9 Flat Config
If you’re on ESLint 9, use the flat config format natively:
// eslint.config.js (ESLint 9 flat config)
import js from '@eslint/js';
import globals from 'globals';
export default [
// Apply recommended rules to all JS files
js.configs.recommended,
{
// Files this config applies to
files: ['**/*.{js,mjs,cjs}'],
// Global variables available
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
ecmaVersion: 2022,
sourceType: 'module',
},
rules: {
'no-console': 'warn',
'no-unused-vars': 'error',
eqeqeq: ['error', 'always'],
},
},
// Ignore patterns (replaces .eslintignore)
{
ignores: ['dist/**', 'node_modules/**', '*.min.js'],
},
];ESLint 9 with TypeScript:
// eslint.config.js
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import globals from 'globals';
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
globals: globals.browser,
parserOptions: {
project: './tsconfig.json', // Required for type-aware rules
},
},
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
},
},
{
// Different rules for test files
files: ['**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
},
},
);Fix 3: Fix Legacy Config (ESLint 8 and Below)
If you’re on ESLint 8 and using .eslintrc.*:
// .eslintrc.json — legacy format
{
"env": {
"browser": true,
"es2022": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"plugins": ["react", "@typescript-eslint"],
"rules": {
"no-console": "warn",
"react/prop-types": "off"
},
"overrides": [
{
// TypeScript-specific rules
"files": ["**/*.ts", "**/*.tsx"],
"rules": {
"@typescript-eslint/no-explicit-any": "error"
}
},
{
// Relax rules in test files
"files": ["**/*.test.*", "**/*.spec.*", "tests/**/*"],
"rules": {
"no-console": "off",
"@typescript-eslint/no-explicit-any": "off"
}
}
],
"ignorePatterns": ["dist/", "node_modules/", "*.config.js"]
}Verify the extends order — later entries override earlier ones:
{
"extends": [
"eslint:recommended",
"plugin:react/recommended", // Adds React rules
"prettier" // Must be LAST — disables formatting rules
]
}Fix 4: Fix Plugin Resolution Errors
# Plugin not found — check it's actually installed
npm ls eslint-plugin-react
# If not listed: install it
npm install --save-dev eslint-plugin-react
# Verify the package resolves
node -e "require('eslint-plugin-react')"
# In monorepos, plugins must be in the root node_modules
# (not just in the package's node_modules)
npm install --save-dev eslint-plugin-react -w . # Install at workspace rootPlugin name mismatch in flat config:
// In legacy config, plugins are referenced as strings:
// "plugins": ["react"] → loads eslint-plugin-react
// In flat config, you import the module directly:
import reactPlugin from 'eslint-plugin-react';
export default [
{
plugins: {
react: reactPlugin, // 'react' is the namespace in rules
},
rules: {
'react/jsx-uses-react': 'error', // Namespace matches key above
},
},
];Common plugin imports for flat config:
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import reactPlugin from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
import importPlugin from 'eslint-plugin-import';
import prettierConfig from 'eslint-config-prettier';
export default [
js.configs.recommended,
...tseslint.configs.recommended,
{
plugins: {
react: reactPlugin,
'react-hooks': reactHooksPlugin,
import: importPlugin,
},
rules: {
...reactPlugin.configs.recommended.rules,
...reactHooksPlugin.configs.recommended.rules,
'import/no-unresolved': 'error',
},
},
prettierConfig, // Disable formatting rules (must be last)
];Fix 5: Fix Rules Not Applying to Certain Files
If rules work in some files but not others, the issue is usually file matching:
// eslint.config.js — check files glob patterns
export default [
{
files: ['src/**/*.js'], // Only applies to .js in src/
rules: { 'no-console': 'error' },
},
// If you have .mjs or .cjs files, they won't match '**/*.js'
{
files: ['**/*.{js,mjs,cjs}'], // More inclusive pattern
rules: { 'no-console': 'error' },
},
];Debug which config applies to a file:
# See the full resolved config for a specific file
npx eslint --print-config src/components/Button.tsx
# Check if a file is being ignored
npx eslint --debug src/components/Button.tsx 2>&1 | grep "Skipping"
# List all files ESLint will lint
npx eslint --print-config --debug . 2>&1 | grep "Linting:"Common file matching mistakes:
// WRONG — doesn't match .tsx files
files: ['**/*.ts']
// CORRECT — matches both .ts and .tsx
files: ['**/*.{ts,tsx}']
// WRONG — doesn't match files in subdirectories
files: ['src/*.ts']
// CORRECT — matches recursively
files: ['src/**/*.ts']
// WRONG — relative path issue in some setups
files: ['./src/**/*.ts'] // Leading './' may cause issues
// CORRECT
files: ['src/**/*.ts']Fix 6: Set Up ESLint with VS Code
Common VS Code ESLint extension issues:
// .vscode/settings.json
{
// Enable ESLint for these file types
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
// Fix on save
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
// Use the local ESLint (not global)
"eslint.useFlatConfig": true, // For ESLint 9 flat config
// Show ESLint status in status bar
"eslint.alwaysShowStatus": true
}If the extension shows “ESLint server is not running”:
# Check ESLint is installed locally
npm ls eslint
# Check the ESLint output panel in VS Code
# View → Output → ESLint
# Common fix: the extension uses the wrong ESLint version
# Add to settings.json:
# "eslint.nodePath": "./node_modules"Still Not Working?
Multiple config files — ESLint picks only one — in legacy config mode, ESLint uses the closest config file to the linted file. If you have an .eslintrc.json in a subdirectory, it overrides the root config for that directory. Use root: true in the root config to stop ESLint from searching further up:
// Root .eslintrc.json
{
"root": true,
"rules": { ... }
}eslint-config-prettier must be last — eslint-config-prettier disables all formatting-related ESLint rules to avoid conflicts with Prettier. It must be the last item in extends (legacy) or the last item in the flat config array. Placing it earlier means a later config re-enables the formatting rules.
Rules from extends not applying — if you override a rule in rules with "off", it disables the rule even if extends enables it. Check if you’ve accidentally disabled a rule you want to keep.
For related ESLint issues, see Fix: ESLint Import No Unresolved and Fix: ESLint Parsing Error Unexpected Token.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: ESLint no-unused-vars False Positives and Configuration
How to fix ESLint no-unused-vars false positives — TypeScript types, destructuring ignores, React imports, function arguments, and configuring the rule to match your codebase patterns.
Fix: CodeMirror Not Working — Editor Not Rendering, Extensions Not Loading, or React State Out of Sync
How to fix CodeMirror 6 issues — basic setup, language and theme extensions, React integration, vim mode, collaborative editing, custom keybindings, and read-only mode.
Fix: GSAP Not Working — Animations Not Playing, ScrollTrigger Not Firing, or React Cleanup Issues
How to fix GSAP animation issues — timeline and tween basics, ScrollTrigger setup, React useGSAP hook, cleanup and context, SplitText, stagger animations, and Next.js integration.
Fix: i18next Not Working — Translations Missing, Language Not Switching, or Namespace Errors
How to fix i18next issues — react-i18next setup, translation file loading, namespace configuration, language detection, interpolation, pluralization, and Next.js integration.