Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

eslint-plugin-react-refresh

ArnaudBarre9.4mMIT0.4.19TypeScript support: included

Validate that your components can safely be updated with Fast Refresh

eslint, eslint-plugin, react, react-refresh, fast refresh

readme

eslint-plugin-react-refresh npm

Validate that your components can safely be updated with Fast Refresh.

Explainer

"Fast Refresh", also known as "hot reloading", is a feature in many modern bundlers. If you update some React component(s) on disk, then the bundler will know to update only the impacted parts of your page -- without a full page reload.

eslint-plugin-react-refresh enforces that your components are structured in a way that integrations such as react-refresh expect.

Limitations

⚠️ To avoid false positives, by default this plugin is only applied on tsx & jsx files. See Options to run on JS files. ⚠️

The plugin relies on naming conventions (i.e. use PascalCase for components, camelCase for util functions). This is why there are some limitations:

  • export * are not supported and will be reported as an error
  • Anonymous function are not supported (i.e export default function() {})
  • Class components are not supported
  • All-uppercase function export is considered an error when not using direct named export (ex const CMS = () => <></>; export { CMS })

Installation

npm i -D eslint-plugin-react-refresh

Usage

This plugin provides a single rule, react-refresh/only-export-components. There are multiple ways to enable it.

import reactRefresh from "eslint-plugin-react-refresh";

export default [
  /* Main config */
  reactRefresh.configs.recommended,
];

Vite config

This enables the allowConstantExport option which is supported by Vite React plugins.

import reactRefresh from "eslint-plugin-react-refresh";

export default [
  /* Main config */
  reactRefresh.configs.vite,
];

Without config

import reactRefresh from "eslint-plugin-react-refresh";

export default [
  {
    // in main config for TSX/JSX source files
    plugins: {
      "react-refresh": reactRefresh,
    },
    rules: {
      "react-refresh/only-export-components": "error",
    },
  },
];

Legacy config

{
  "plugins": ["react-refresh"],
  "rules": {
    "react-refresh/only-export-components": "error"
  }
}

Examples

These examples are from enabling react-refresh/only-exports-components.

Fail

export const foo = () => {};
export const Bar = () => <></>;
export default function () {}
export default compose()(MainComponent)
export * from "./foo";
const Tab = () => {};
export const tabs = [<Tab />, <Tab />];
const App = () => {};
createRoot(document.getElementById("root")).render(<App />);

Pass

export default function Foo() {
  return <></>;
}
const foo = () => {};
export const Bar = () => <></>;
import { App } from "./App";
createRoot(document.getElementById("root")).render(<App />);

Options

These options are all present on react-refresh/only-exports-components.

interface Options {
  allowExportNames?: string[];
  allowConstantExport?: boolean;
  customHOCs?: string[];
  checkJS?: boolean;
}

const defaultOptions: Options = {
  allowExportNames: [],
  allowConstantExport: false,
  customHOCs: [],
  checkJS: false,
};

allowExportNames (v0.4.4)

Default: []

If you use a framework that handles HMR of some specific exports, you can use this option to avoid warning for them.

Example for Remix:

{
  "react-refresh/only-export-components": [
    "error",
    { "allowExportNames": ["meta", "links", "headers", "loader", "action"] }
  ]
}

allowConstantExport (v0.4.0)

Default: false (true in vite config)

Don't warn when a constant (string, number, boolean, templateLiteral) is exported aside one or more components.

This should be enabled if the fast refresh implementation correctly handles this case (HMR when the constant doesn't change, propagate update to importers when the constant changes.). Vite supports it, PR welcome if you notice other integrations works well.

{
  "react-refresh/only-export-components": [
    "error",
    { "allowConstantExport": true }
  ]
}

Enabling this option allows code such as the following:

export const CONSTANT = 3;
export const Foo = () => <></>;

checkJS (v0.3.3)

Default: false

If your using JSX inside .js files (which I don't recommend because it forces you to configure every tool you use to switch the parser), you can still use the plugin by enabling this option. To reduce the number of false positive, only files importing react are checked.

{
  "react-refresh/only-export-components": ["error", { "checkJS": true }]
}

customHOCs (v0.4.15)

If you're exporting a component wrapped in a custom HOC, you can use this option to avoid false positives.

{
  "react-refresh/only-export-components": [
    "error",
    { "customHOCs": ["observer", "withAuth"] }
  ]
}

changelog

Changelog

0.4.19

Add name to configs for ESLint Config Inspector

0.4.18

ESM/CJS interop is the worse that happened to this ecosystem, this is all I have to say.

0.4.17

  • Fix detection of local components to not generate warning on for variable inside JSX files that follow React component naming (fixes #75)
  • Update types to not require extra unnecessary .default property access under TS node16 module resolution (fixes #70)

0.4.16

Fix CJS/ESM interop issue. Sorry everyone for the trouble.

0.4.15

Add support for custom HOCs (#60)

By default, the rule only knows that memo & forwardRef function calls with return a React component. With this option, you can also allow extra function names like Mobx observer to make this code valid:

const Foo = () => <></>;
export default observer(Foo);
{
  "react-refresh/only-export-components": [
    "error",
    { "customHOCs": ["observer"] }
  ]
}

Thanks @HorusGoul!

You can now add the recommended config to your ESLint config like this:

import reactRefresh from "eslint-plugin-react-refresh";

export default [
  /* Main config */
  reactRefresh.configs.recommended, // Or reactRefresh.configs.vite for Vite users
];

To follow ESLint recommandations, the rule is added with the error severity.

Some simple types ensure that people typecheking their config won't need @ts-expect-error anymore.

Bump ESLint peer dependency to 8.40

This was actually done by mistake in the previous release when moving from a deprecated API to a new one.

Given that ESLint 8 is officialy end-of-life and the only report (#56) didn't get likes, I'm going forward and documenting the expected minimum version from ESLin in the package JSON so that people can get warning from their package manager.

0.4.14

  • Warn if a context is exported alongside a component (fixes #53). Thanks @IgorAufricht!

0.4.13

  • Support for react-redux connect (export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)) (fixes #51)
  • Support for Arbitrary Module Identifiers syntax (fixes #52)

0.4.12

  • Support type assertion on default export (fixes #48)
  • Add default export to fix usage with jiti (fixes #50)

0.4.11

  • Ignore type exports (ex. export type foo = string;) (fixes #47)

0.4.10

  • Support function Foo() {}; export default React.memo(Foo) (#46) (thanks @SukkaW!)

0.4.9

  • Support function Foo() {}; export default memo(Foo) (fixes #44) (thanks @SukkaW!)

0.4.8

  • Support export const foo = -1 with allowConstantExport (fixes #43)

0.4.7

  • Support export { Component as default } (fixes #41)

0.4.6

  • Ignore cypress test files (#39)

0.4.5

  • Allow TaggedTemplateExpression for styled components (fixes #32)

0.4.4

  • Add allowExportNames option (fixes #29)
  • Support memo default export function components (fixes #27)
  • Warn on export expressions that are not React component (array, object, logical expression, ...) (fixes #26)

0.4.3

  • Add warning for TS enums exports

0.4.2

  • Fix typos in messages (#15, #16). Thanks @adamschachne & @janikga!

0.4.1

  • Ignore export type * (fixes #12)
  • Support for all-uppercase function wrapped in forwardRef/memo (#11)

0.4.0

Add allowConstantExport option (fixes #8)

This option allow to don't warn when a constant (string, number, boolean, templateLiteral) is exported aside one or more components.

This should be enabled if the fast refresh implementation correctly handles this case (HMR when the constant doesn't change, propagate update to importers when the constant changes). Vite supports it, PR welcome if you notice other integrations works well.

Allow all-uppercase function exports (fixes #11)

This only works when using direct export. So this pattern doesn't warn anymore:

export const CMS = () => <></>;

But this one will still warn:

const CMS = () => <></>;
export default CMS;

0.3.5

Ignore stories files (*.stories.*) (fixes #10)

0.3.4

Report default CallExpression exports (#7) (fixes #6)

This allows to report a warning for this kind of patterns that creates anonymous components:

export default compose()(MainComponent)

0.3.3

Add checkJS option (#5) (fixes #4)

0.3.2

Ignore test files (*.test.*, *.spec.*) (fixes #2)

0.3.1

Allow numbers in component names (fixes #1)

0.3.0

Report an error when a file that contains components that can't be fast-refreshed because:

  • There are no export (entrypoint)
  • Exports are not components

0.2.1

Doc only: Update limitations section README.md

0.2.0

  • Fix TS import
  • Document limitations

0.1.0

Initial release