Skip to content

Fix: ESLint Config Not Working — Rules Ignored, Flat Config Errors, or Plugin Not Found

FixDevs ·

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 error

Or 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 defaulteslint.config.js (or .mjs/.cjs). The old extends, .eslintrc.* format, and eslintignore are no longer supported without a compatibility layer.
  • ESLint 8 and below use legacy config.eslintrc.json, .eslintrc.js, .eslintrc.yaml. extends is 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 overrides or flat config files glob 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 root

Plugin 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 lasteslint-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.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles