Skip to content

eslint-plugin-react-hooks & "Flat Config" (ESLint 9) #28313

Closed

Description

@JoshuaKGoldberg

👋 Coming over from eslint/eslint#18093: ESLint is migrating to a new "flat config" format that will be the default in ESLint v9.

It doesn't look like eslint-plugin-react-hooks has documented support yet. But, based on searching around (e.g. vercel/next.js#49337), ESLint v9 is basically supported if you wire it up manually in your config:

// eslint.config.js
import eslint from "@eslint/js";
import hooksPlugin from "eslint-plugin-react-hooks";

export default [
  eslint.configs.recommended,
  {
    plugins: {
      "react-hooks": hooksPlugin,
    },
    rules: hooksPlugin.configs.recommended.rules,
  },
];

Most community plugins provide a more convenient wrapper. For example, eslint-plugin-jsdoc provides a jsdoc.configs['flat/recommended'] object:

// eslint.config.js
import jsdoc from 'eslint-plugin-jsdoc';

export default [
  jsdoc.configs['flat/recommended'],
];

Would the React team be open to a PR adding in a preset object like that? And either way, updating the docs on https://www.npmjs.com/package/eslint-plugin-react-hooks?

Note: this was also filed as reactjs/react.dev#6430.

I'm posting this issue here as a reference & cross-linking it to the table in eslint/eslint#18093. If there's anything technical blocking the extension from working with flat configs, please let us know - we'd be happy to try to help! 💜

Additional resources:

(sorry for not using the issue templates - I wasn't sure whether this would count as a bug)

Activity

  1. eps1lon commented on Feb 13, 2024

    @eps1lon
    Collaborator

    Thank you for reaching out. What would be involved in creating such a wrapper? Does it make more sense to file a PR and then see how it'll look?

    We're only on ESLint 7 though so I guess we need to upgrade that first to test.

  2. JoshuaKGoldberg commented on Feb 13, 2024

    @JoshuaKGoldberg
    ContributorAuthor

    What would be involved in creating such a wrapper?

    It's not too much work (famous last words, I know). In theory the plugin should be able to get away with exporting an object containing configs, meta, and rules. https://eslint.org/docs/latest/extend/plugin-migration-flat-config#adding-plugin-meta-information shows a very minimal object.

    make more sense to file a PR and then see how it'll look?

    👍

    We're only on ESLint 7 though so I guess we need to upgrade that first to test.

    Yeah it feels like it'd probably be cleanest to upgrade to ESLint 8 first, just in case any breaking changes impact the plugin.

  3. RoystonS commented on Apr 5, 2024

    @RoystonS

    Note that even wiring up the plugin manually doesn't enable it to work with the now-released ESLint 9: the plugin currently uses APIs like context.getSource which don't exist any more (eslint/eslint#17520), so there'll need to be some upgrade work.

    e.g. https://github.com/facebook/react/blob/2acfb7b60922bdc8376dd144ca7bc532df78254b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js#L529

  4. JstnMcBrd commented on Apr 6, 2024

    @JstnMcBrd

    Eslint officially released v9.0.0 today. However, the eslint-plugin-react-hooks plugin still restricts eslint to v8 in the peerDependencies list, so nobody who uses the plugin can download v9 without breaking dependency resolution.

    Hope you'll add support for v9 soon!

  5. RoystonS commented on Apr 6, 2024

    @RoystonS

    Should we raise a separate issue for v9 API support?

  6. yhx-12243 commented on Apr 6, 2024

    @yhx-12243

    Should we raise a separate issue for v9 API support?

    Currently under discussion in jsx-eslint/eslint-plugin-react#3699.

  7. eps1lon commented on Apr 6, 2024

    @eps1lon
    Collaborator

    ESLint v9 support will be done in #28773

  8. josiahgallen commented on Apr 18, 2024

    @josiahgallen

    @eps1lon how far away from merge/release is this update to support eslint v9?

  9. virtuallyunknown commented on Apr 22, 2024

    @virtuallyunknown

    ESLint v9 support will be done in #28773

    Hey @eps1lon, greetings!

    I just wanted to ask, is #28773 part of the 4.6.0 release that was published on npm 3 days ago or it's planned for a future release?

    I am currently in the process of migrating to flat config, and the plugin is giving me an error.

    Oops! Something went wrong! :(
    
    ESLint: 9.1.0
    
    TypeError: context.getSource is not a function
    Occurred while linting /home/user/projects/eslint-flat-config-migration/src/index.tsx:8
    Rule: "react-hooks/exhaustive-deps"
        at visitFunctionWithDependencies (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint-plugin-react-hooks@4.6.0_eslint@9.1.0/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js:1704:42)
        at visitCallExpression (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint-plugin-react-hooks@4.6.0_eslint@9.1.0/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js:1759:11)
        at ruleErrorHandler (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/linter.js:1115:48)
        at /home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/safe-emitter.js:45:58
        at Array.forEach (<anonymous>)
        at Object.emit (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/safe-emitter.js:45:38)
        at NodeEventGenerator.applySelector (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/node-event-generator.js:297:26)
        at NodeEventGenerator.applySelectors (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/node-event-generator.js:326:22)
        at NodeEventGenerator.enterNode (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/node-event-generator.js:340:14)
        at runRules (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/linter.js:1154:40)
    

    If needed, here is a my repository where the issue can be reproduced by running npx eslint src/index.tsx

    https://github.com/virtuallyunknown/eslint-flat-config-migration

    Relevant files:
    https://github.com/virtuallyunknown/eslint-flat-config-migration/blob/master/eslint.config.js
    https://github.com/virtuallyunknown/eslint-flat-config-migration/blob/master/configs/react.js

    Cheers!

  10. JacobZyy commented on Apr 22, 2024

    @JacobZyy

    ESLint v9 support will be done in #28773

    Hey @eps1lon, greetings!

    I just wanted to ask, is #28773 part of the 4.6.0 release that was published on npm 3 days ago or it's planned for a future release?

    I am currently in the process of migrating to flat config, and the plugin is giving me an error.

    Oops! Something went wrong! :(
    
    ESLint: 9.1.0
    
    TypeError: context.getSource is not a function
    Occurred while linting /home/user/projects/eslint-flat-config-migration/src/index.tsx:8
    Rule: "react-hooks/exhaustive-deps"
        at visitFunctionWithDependencies (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint-plugin-react-hooks@4.6.0_eslint@9.1.0/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js:1704:42)
        at visitCallExpression (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint-plugin-react-hooks@4.6.0_eslint@9.1.0/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js:1759:11)
        at ruleErrorHandler (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/linter.js:1115:48)
        at /home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/safe-emitter.js:45:58
        at Array.forEach (<anonymous>)
        at Object.emit (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/safe-emitter.js:45:38)
        at NodeEventGenerator.applySelector (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/node-event-generator.js:297:26)
        at NodeEventGenerator.applySelectors (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/node-event-generator.js:326:22)
        at NodeEventGenerator.enterNode (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/node-event-generator.js:340:14)
        at runRules (/home/user/projects/eslint-flat-config-migration/node_modules/.pnpm/eslint@9.1.0/node_modules/eslint/lib/linter/linter.js:1154:40)
    

    If needed, here is a my repository where the issue can be reproduced by running npx eslint src/index.tsx

    https://github.com/virtuallyunknown/eslint-flat-config-migration

    Relevant files: https://github.com/virtuallyunknown/eslint-flat-config-migration/blob/master/eslint.config.js https://github.com/virtuallyunknown/eslint-flat-config-migration/blob/master/configs/react.js

    Cheers!

    got the same error and I turn off the rules about react-hooks...

  11. smaven commented on Apr 25, 2024

    @smaven

    The issue seems to have been resolved in the canary release.

    If you are using eslint-plugin-react, some rules such as react/display-name or other recommended ones don't work: jsx-eslint/eslint-plugin-react#3699.

  12. eps1lon commented on Apr 25, 2024

    @eps1lon
    Collaborator

    If you are using eslint-plugin-react

    eslint-plugin-react is not authored by the React team. Please file an issue for this plugin in https://github.com/jsx-eslint/eslint-plugin-react instead.

    ESLint v9 support is available in the Canary release channel (earliest version 5.1.0-canary-cb151849e1-20240424) and will be released as stable with the stable release of React 19.

  13. virtuallyunknown commented on Apr 25, 2024

    @virtuallyunknown

    I am not sure if this feedback is of any value, but it looks like the types from eslint and typescript-eslint are signaling about potential issues.

    // @ts-check
    
    // @typescript-eslint/utils: 7.7.1
    // eslint-plugin-react-hooks: 5.1.0-canary-cb151849e1-20240424
    
    import eslintPluginReactHooks from 'eslint-plugin-react-hooks';
    
    /** @type {import('eslint').Linter.FlatConfig[]} */
    export const configuration1 = [
        {
            plugins: {
                'react-hooks': eslintPluginReactHooks
            }
        }
    ];
    
    /** @type {import('@typescript-eslint/utils').TSESLint.FlatConfig.ConfigFile} */
    export const configuration2 = [
        {
            plugins: {
                'react-hooks': eslintPluginReactHooks
            }
        }
    ];

    I don't want to paste the error message because it's a giant wall of text, but feel free to try it on your own.

    This appears to be a type error only, and as far as I can tell the plugin is working as expected, so thanks for the updates, much appreciated!

  14. movestill commented on May 8, 2024

    @movestill

    I'm getting this error using v4.6.2 with ESLint 9.1.1 and React 18.2.0:

    ESLint: 9.1.1
    
    TypeError: context.getScope is not a function
    Occurred while linting /Users/me/Documents/frontend/src/components/CTable/CTable.tsx:1013
    Rule: "react-hooks/exhaustive-deps"
        at visitCallExpression (/Users/me/Documents/frontend/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js:1780:34)
        at ruleErrorHandler (/Users/me/Documents/frontend/node_modules/eslint/lib/linter/linter.js:1115:48)
        at /Users/me/Documents/frontend/node_modules/eslint/lib/linter/safe-emitter.js:45:58
        at Array.forEach (<anonymous>)
        at Object.emit (/Users/me/Documents/frontend/node_modules/eslint/lib/linter/safe-emitter.js:45:38)
        at NodeEventGenerator.applySelector (/Users/me/Documents/frontend/node_modules/eslint/lib/linter/node-event-generator.js:297:26)
        at NodeEventGenerator.applySelectors (/Users/me/Documents/frontend/node_modules/eslint/lib/linter/node-event-generator.js:326:22)
        at NodeEventGenerator.enterNode (/Users/me/Documents/frontend/node_modules/eslint/lib/linter/node-event-generator.js:340:14)
        at runRules (/Users/me/Documents/frontend/node_modules/eslint/lib/linter/linter.js:1154:40)
        at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/Users/me/Documents/frontend/node_modules/eslint/lib/linter/linter.js:1880:31)
    
  15. 92 remaining items

  16. alex-r-bigelow commented on Dec 20, 2024

    @alex-r-bigelow

    FWIW, for anyone already using FlatCompat for other plugins that don't yet have a flat config (e.g. the workaround some people seem to be suggesting for prettier), it also seems to work for react-hooks:

    import { FlatCompat } from '@eslint/eslintrc';
    import pluginJs from '@eslint/js';
    import pluginReact from 'eslint-plugin-react';
    import tseslint from 'typescript-eslint';
    
    const compat = new FlatCompat();
    
    /** @type {import('eslint').Linter.Config[]} */
    export default [
      {
        files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'],
      },
      pluginJs.configs.recommended,
      ...tseslint.configs.recommended,
      pluginReact.configs.flat.recommended,
      ...compat.extends('plugin:prettier/recommended'),
      ...compat.extends('plugin:react-hooks/recommended'),
      {
        rules: {
          'no-debugger': 'warn',
          'no-fallthrough': ['error', { commentPattern: 'break[\\s\\w]*omitted' }],
          'react/react-in-jsx-scope': 'off',
          '@typescript-eslint/no-unused-vars': ['warn'],
        },
      },
    ];
  17. mrmckeb commented on Dec 24, 2024

    @mrmckeb

    Just a quick note that the new eslint-plugin-react-compiler package also doesn't support ESLint 9 / FlatConfig.

  18. virtuallyunknown commented on Dec 24, 2024

    @virtuallyunknown

    Just a quick note that the new eslint-plugin-react-compiler package also doesn't support ESLint 9 / FlatConfig.

    If you are certain about this, then it's definitely worth opening a separate issue about it.

  19. mrmckeb commented on Dec 28, 2024

    @mrmckeb

    If you are certain about this, then it's definitely worth opening a separate issue about it.

    My thought was that it would make sense to address both at once, as they're both published from within this repository.
    https://github.com/facebook/react/tree/main/compiler/packages/eslint-plugin-react-compiler

    I'm happy to raise a PR for both if the team managing this repo want a hand?

  20. michaelfaith commented on Dec 28, 2024

    @michaelfaith
    Contributor

    I'm happy to raise a PR for both if the team managing this repo want a hand?

    I already have a PR open for hooks, and would gladly do the other, but there are unresolved decisions that the team needs to provide input on the hooks PR, which would apply to the compiler change too. So, no need to create yet another PR, until that's resolved.

  21. Flaysh commented on Jan 9, 2025

    @Flaysh

    Hi, is there any update on this issue?

    I’m trying to integrate eslint-plugin-react-hooks into my ESLint configuration using tseslint.config([]) but haven’t had any success so far. I’ve tried all the suggested solutions mentioned above, but still can’t get it to work.
    I’m currently using ESLint v9. Could you provide guidance or an example to help me set up this plugin properly with tseslint?
    Thank you!

  22. laug commented on Jan 9, 2025

    @laug

    @Flaysh here is how I do it:

    At the top of eslint.config.js:
    import reactHooks from 'eslint-plugin-react-hooks'

    Under extends: nothing, as this requires the flat config

    Under plugins:

    plugins: {
          'react': react,
          'react-hooks': reactHooks,
          // ...
    }
    

    Under rules:

    rules: {
          ...reactHooks.configs.recommended.rules,
          // ...
    }
    
  23. kachkaev commented on Jan 9, 2025

    @kachkaev
    Contributor

    @Flaysh this worked for me:

    eslint.config.js

    import eslintPluginReact from "eslint-plugin-react";
    import eslintPluginReactHooks from "eslint-plugin-react-hooks";
    
    // ...
    
      eslintPluginReact.configs.flat?.["recommended"] ?? {},
      eslintPluginReact.configs.flat?.["jsx-runtime"] ?? {},
      {
        rules: {
          "react/jsx-boolean-value": ["error", "always"],
          "react/jsx-curly-brace-presence": "error",
          "react/no-unknown-property": "error",
          "react/prop-types": "off", // handled by TypeScript
          "react/self-closing-comp": "error",
        },
        settings: { react: { version: "detect" } },
      },
    
      // TODO: Simplify when https://github.com/facebook/react/issues/28313 is resolved
      {
        plugins: {
          "react-hooks": eslintPluginReactHooks,
        },
        rules: { ...eslintPluginReactHooks.configs.recommended.rules },
      },
    
    // ...

    declarations/eslint-plugin-react-hooks.d.ts

    // TODO: Remove when https://github.com/facebook/react/issues/30119 is resolved
    
    declare module "eslint-plugin-react-hooks" {
      import type { Linter, Rule } from "eslint";
    
      export const configs: {
        recommended: Linter.Config;
      };
    
      declare const rules: {
        "rules-of-hooks": Rule.RuleModule;
        "exhaustive-deps": Rule.RuleModule;
      };
    
      declare const plugin: {
        configs: typeof configs;
        rules: typeof rules;
      };
    
      export default plugin;
    }
  24. brasillero commented on Feb 5, 2025

    @brasillero

    FWIW, for anyone already using FlatCompat for other plugins that don't yet have a flat config (e.g. the workaround some people seem to be suggesting for prettier), it also seems to work for react-hooks:

    import { FlatCompat } from '@eslint/eslintrc';
    import pluginJs from '@eslint/js';
    import pluginReact from 'eslint-plugin-react';
    import tseslint from 'typescript-eslint';

    const compat = new FlatCompat();

    /** @type {import('eslint').Linter.Config[]} /
    export default [
    {
    files: ['**/
    .{js,mjs,cjs,ts,jsx,tsx}'],
    },
    pluginJs.configs.recommended,
    ...tseslint.configs.recommended,
    pluginReact.configs.flat.recommended,
    ...compat.extends('plugin:prettier/recommended'),
    ...compat.extends('plugin:react-hooks/recommended'),
    {
    rules: {
    'no-debugger': 'warn',
    'no-fallthrough': ['error', { commentPattern: 'break[\s\w]*omitted' }],
    'react/react-in-jsx-scope': 'off',
    '@typescript-eslint/no-unused-vars': ['warn'],
    },
    },
    ];

    This worked for me, but I got the solution from eslint docs here.

    My code:

    import { FlatCompat } from '@eslint/eslintrc';
    import pluginJs from '@eslint/js';
    import pluginReact from 'eslint-plugin-react';
    import reactHooks from 'eslint-plugin-react-hooks';
    import reactRefresh from 'eslint-plugin-react-refresh';
    import globals from 'globals';
    import path from 'path';
    import tseslint from 'typescript-eslint';
    import { fileURLToPath } from 'url';
    
    // mimic CommonJS variables -- not needed if using CommonJS
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = path.dirname(__filename);
    
    const compat = new FlatCompat({
      baseDirectory: __dirname,
    });
    
    /** @type {import('eslint').Linter.Config[]} */
    const config = [
      { files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'] },
      { languageOptions: { globals: globals.browser } },
      pluginJs.configs.recommended,
      ...tseslint.configs.recommended,
      pluginReact.configs.flat.recommended,
      reactRefresh.configs.recommended,
      ...compat.extends('plugin:react-hooks/recommended'),
    ];
    
    export default config;
    
  25. Neoxs commented on Mar 23, 2025

    @Neoxs

    🚀 Just completed an ESLint v9 migration, and wow… what a ride! 😵‍💫

    I ran into breaking changes, weird errors, and unexpected surprises—but I documented everything so you don’t have to suffer the same way.

    If you’re upgrading (or still hesitating), this guide might save you some headaches:
    📖 neoxs.me/blog/migration-to-eslint-v9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions