Search code examples
javascriptreactjswebpackwebpack-dev-serverwebpack-5

React Fast Refresh doesn't work with Webpack 5


When I use React Fast Refresh with Webpack 5, the whole page is refreshed instead of just the component that changed.


EDIT the issue was caused by Webpack configuration, a proper configuration to fix the problem is posted as an answer below.


Solution

  • I figured out the issue after looking into the official examples (thanks to @Keith for pointing me towards them).

    The main issue was my index.html included a link to the bundle.js that wasn't needed. My Webpack configuration also included extra unnecessary configurations like devMiddleware & static properties.

    Here's my folder structure:

    .
    ├── README.md
    ├── babel.config.js
    ├── package-lock.json
    ├── package.json
    ├── public
    │   └── index.html
    ├── src
    │   ├── Hello.jsx
    │   └── index.jsx
    └── webpack.config.js
    

    I'm providing my index.html, webpack.config.js, and bable.config.js here for future references:

    webpack.config.js

    const path = require("path");
    const ReactRefreshPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    
    const isDevelopment = process.env.NODE_ENV !== "production";
    
    module.exports = {
      mode: isDevelopment ? "development" : "production",
    
      devServer: {
        hot: true,
    
        client: {
          logging: "error",
          overlay: true,
        },
      },
    
      entry: ["./src/index.jsx"],
    
      module: {
        rules: [
          {
            test: /\.jsx?$/,
            include: path.join(__dirname, "src"),
            use: "babel-loader",
          },
        ],
      },
    
      plugins: [
        isDevelopment && new ReactRefreshPlugin(),
        new HtmlWebpackPlugin({
          filename: "./index.html",
          template: "./public/index.html",
        }),
      ].filter(Boolean),
    
      resolve: {
        extensions: [".js", ".jsx"],
      },
    };
    

    index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Webpack 5 And React Fast Refresh</title>
      </head>
      <body>
        <div id="app"></div>
      </body>
    </html>
    

    babel.config.js

    module.exports = (api) => {
      // This caches the Babel config
      api.cache.using(() => process.env.NODE_ENV);
      return {
        presets: [
          "@babel/preset-env",
          // Enable development transform of React with new automatic runtime
          [
            "@babel/preset-react",
            { development: !api.env("production"), runtime: "automatic" },
          ],
        ],
        // Applies the react-refresh Babel plugin on non-production modes only
        ...(!api.env("production") && { plugins: ["react-refresh/babel"] }),
      };
    };