Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to migrate my CRA .eslintrc.js config to eslint.config.js in a new Vite + React 19 app

I'm trying to set up the same ESLint configuration in my new React 19 + Vite app using the modern eslint.config.js format. However, it's not working — the linter doesn’t show any errors or warnings in the editor. New eslint.config.js

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

import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import prettier from "eslint-plugin-prettier";
import importPlugin from "eslint-plugin-import";
import react from "eslint-plugin-react";
import jsxA11y from "eslint-plugin-jsx-a11y";

export default defineConfig([
  { ignores: ["dist"] },
  {
    files: ["**/*.{js,mjs,cjs,jsx}"],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
      parserOptions: {
        ecmaVersion: "latest",
        ecmaFeatures: { jsx: true },
        sourceType: "module",
      },
    },
    plugins: {
      "react-hooks": reactHooks,
      "react-refresh": reactRefresh,
      prettier,
      import: importPlugin,
      react,
      "jsx-a11y": jsxA11y,
    },
    rules: {
      ...js.configs.recommended.rules,
      ...reactHooks.configs.recommended.rules,
      // "no-unused-vars": ["error", { varsIgnorePattern: "^[A-Z_]" }],
      // "no-undef": ["error", { globals: { process: true } }],
      // Vite React specific
      "react-refresh/only-export-components": [
        "warn",
        { allowConstantExport: true },
      ],

      // Your custom rules from old config
      "prettier/prettier": "off",
      "no-nested-ternary": "off",
      "no-underscore-dangle": "off",
      "default-param-last": "off",
      "prefer-arrow-callback": "error",
      camelcase: ["error", { properties: "never", ignoreDestructuring: true }],
      "no-else-return": ["error", { allowElseIf: true }],
      "no-param-reassign": ["error", { props: false }],

      // Import rules
      "import/no-extraneous-dependencies": [
        "error",
        {
          devDependencies: false,
          optionalDependencies: false,
          peerDependencies: false,
        },
      ],
      "import/order": [
        "error",
        {
          pathGroups: [
            {
              pattern: "react",
              group: "external",
              position: "before",
            },
          ],
          pathGroupsExcludedImportTypes: ["builtin"],
        },
      ],
      "import/no-unused-modules": ["error", { unusedExports: true }],

      // React rules
      "react/require-default-props": "off",
      "react/jsx-no-undef": ["error", { allowGlobals: true }],
      "react/jsx-uses-vars": "error",

      // Best Practices
      eqeqeq: "error",
      "no-invalid-this": "error",
      "no-return-assign": "error",
      "no-unused-expressions": ["error", { allowTernary: true }],
      "no-useless-concat": "error",
      "no-useless-return": "error",
      "no-use-before-define": "error",
      "no-duplicate-imports": "error",
      "no-plusplus": ["error", { allowForLoopAfterthoughts: true }],

      // Variable and import rules
      "no-undef": "error",
      "no-unused-vars": [
        "error",
        {
          vars: "all",
          args: "after-used",
          ignoreRestSiblings: true,
          varsIgnorePattern: "^[A-Z_]", // Keep the pattern from new config
        },
      ],
    },
    settings: {
      react: {
        version: "detect",
      },
    },
  },
  pluginReact.configs.recommended,
]);

This is the ESLint config I used successfully in my older CRA app (.eslintrc.js):

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: ["prettier"],

  plugins: ["prettier", "import", "react", "react-hooks", "jsx-a11y"],
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 12,
    sourceType: "module",
  },
  rules: {
    "prettier/prettier": "off",
    "no-nested-ternary": "off",
    "react/require-default-props": "off",
    "no-underscore-dangle": ["off"],
    "default-param-last": "off",
    "prefer-arrow-callback": "error",
    camelcase: ["error", { properties: "never", ignoreDestructuring: true }],
    "no-else-return": ["error", { allowElseIf: true }],
    "no-param-reassign": ["error", { props: false }],
    // Best Practices
    "import/no-extraneous-dependencies": [
      "error",
      {
        devDependencies: false,
        optionalDependencies: false,
        peerDependencies: false,
      },
    ],
    eqeqeq: "error",
    "no-invalid-this": "error",
    "no-return-assign": "error",
    "no-unused-expressions": ["error", { allowTernary: true }],
    "no-useless-concat": "error",
    "no-useless-return": "error",
    "no-use-before-define": "error",
    "no-duplicate-imports": "error",
    "no-plusplus": ["error", { allowForLoopAfterthoughts: true }],
    "import/order": [
      "error",
      {
        pathGroups: [
          {
            pattern: "react",
            group: "external",
            position: "before",
          },
        ],
        pathGroupsExcludedImportTypes: ["builtin"],
      },
    ],
    // Ensure components are imported if used
    "react/jsx-no-undef": ["error", { allowGlobals: true }],
    // Ensure all variables are declared before use
    "no-undef": "error",
    // Ensure there are no unused variables
    "no-unused-vars": ["error", { vars: "all", args: "after-used", ignoreRestSiblings: true }],
    // Ensure there are no unused imports
    "import/no-unused-modules": ["error", { unusedExports: true }],
    // Mark variables used in JSX as used
    "react/jsx-uses-vars": "error",
  },
  settings: {
    react: {
      version: "detect",
    },
  },
};

I did try npm run lint but I get this:

Oops! Something went wrong! :(

ESLint: 9.28.0

ESLint couldn't find a configuration file. To set up a configuration file for this project, please run:

npm init @eslint/config@latest

And if I run this command, it resets full eslint.config.js file

But I’m not sure how to properly convert or apply it to the new flat config (eslint.config.js). Any ideas on how to correctly set it up so ESLint works in the new environment?

like image 519
Jay Avatar asked Dec 18 '25 15:12

Jay


1 Answers

Your flat config has some issues, so let's go one by one:

  1. You're missing an import at the top:
import js from '@eslint/js';

Since you're using the following rules: ...js.configs.recommended.rules.

  1. You're importing eslint-plugin-react twice, this isn't an error but it's better to just import it once.

  2. The last line where you're extending the config pluginReact.configs.recommended will not work because it has an older format for plugins. Considering how you already have the plugins and parserOptions manually added onto your config, you should probably just extend the rules:

rules: {
  ...react.configs.recommended.rules
}
  1. The error you're facing is actually documented here, it is a known issue with eslint-plugin-import when using the import/no-unused-modules rule. You can fix it by adding an empty .eslintrc file in your project.

Finally, your config could look like this:

import js from '@eslint/js';
import globals from "globals";
import { defineConfig } from "eslint/config";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import prettier from "eslint-plugin-prettier";
import importPlugin from "eslint-plugin-import";
import react from "eslint-plugin-react";
import jsxA11y from "eslint-plugin-jsx-a11y";

export default defineConfig([
  { ignores: ["dist"] },
  {
    files: ["**/*.{js,mjs,cjs,jsx}"],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
      parserOptions: {
        ecmaVersion: "latest",
        ecmaFeatures: { jsx: true },
        sourceType: "module",
      },
    },
    plugins: {
      "react-hooks": reactHooks,
      "react-refresh": reactRefresh,
      prettier,
      import: importPlugin,
      react,
      "jsx-a11y": jsxA11y,
    },
    rules: {
      ...js.configs.recommended.rules,
      ...reactHooks.configs.recommended.rules,
      // "no-unused-vars": ["error", { varsIgnorePattern: "^[A-Z_]" }],
      // "no-undef": ["error", { globals: { process: true } }],
      // Vite React specific
      "react-refresh/only-export-components": [
        "warn",
        { allowConstantExport: true },
      ],

      // Your custom rules from old config
      "prettier/prettier": "off",
      "no-nested-ternary": "off",
      "no-underscore-dangle": "off",
      "default-param-last": "off",
      "prefer-arrow-callback": "error",
      camelcase: ["error", { properties: "never", ignoreDestructuring: true }],
      "no-else-return": ["error", { allowElseIf: true }],
      "no-param-reassign": ["error", { props: false }],

      // Import rules
      "import/no-extraneous-dependencies": [
        "error",
        {
          devDependencies: false,
          optionalDependencies: false,
          peerDependencies: false,
        },
      ],
      "import/order": [
        "error",
        {
          pathGroups: [
            {
              pattern: "react",
              group: "external",
              position: "before",
            },
          ],
          pathGroupsExcludedImportTypes: ["builtin"],
        },
      ],
      "import/no-unused-modules": ["error", { unusedExports: true }],

      // React rules
      "react/require-default-props": "off",
      "react/jsx-no-undef": ["error", { allowGlobals: true }],
      "react/jsx-uses-vars": "error",
      ...react.configs.recommended.rules,

      // Best Practices
      eqeqeq: "error",
      "no-invalid-this": "error",
      "no-return-assign": "error",
      "no-unused-expressions": ["error", { allowTernary: true }],
      "no-useless-concat": "error",
      "no-useless-return": "error",
      "no-use-before-define": "error",
      "no-duplicate-imports": "error",
      "no-plusplus": ["error", { allowForLoopAfterthoughts: true }],

      // Variable and import rules
      "no-undef": "error",
      "no-unused-vars": [
        "error",
        {
          vars: "all",
          args: "after-used",
          ignoreRestSiblings: true,
          varsIgnorePattern: "^[A-Z_]", // Keep the pattern from new config
        },
      ],
    },
    settings: {
      react: {
        version: "detect",
      },
    },
  },
]);
like image 193
moonstar-x Avatar answered Dec 21 '25 05:12

moonstar-x



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!