HMR and Hot Reloading with the webpack-dev-server

The webpack-dev-server provides:

  1. Speedy compilation of client side assets
  2. Optional HMR which means that the page will reload automatically when after compilation completes. Note, some developers do not like this, as you'll abruptly lose any tweaks within the Chrome development tools.
  3. Optional hot-reloading. The older react-hot-loader has been deprecated in favor of fast-refresh. For use with webpack, see Client Side rendering and HMR using react-refresh-webpack-plugin section bellow or visit react-refresh-webpack-plugin for additional details.

If you are not using server-side rendering (not using prerender: true), then you can follow all the regular docs for using the bin/webpack-dev-server during development.

Server Side Rendering with the Default rails/webpacker bin/webpack-dev-server

If you are using server-side rendering, then you have a couple options. The recommended technique is to have a different webpack configuration for server rendering.

If you use the same Webpack setup for your server and client bundles

If you do use the webpack-dev-server for prerendering, be sure to set the config/initializers/react_on_rails.rb setting of

  config.same_bundle_for_client_and_server = true

dev_server.hmr maps to devServer.hot. This must be false if you're using the webpack-dev-server for client and server bundles.

dev_server.inline maps to devServer.inline. This must also be false.

If you don't configure these two to false, you'll see errors like:

  • "ReferenceError: window is not defined" (if hmr is true)
  • "TypeError: Cannot read property 'prototype' of undefined" (if inline is true)

Client Side rendering with HMR using react-refresh-webpack-plugin

Basic installation

To enable HMR functionality you have to use ./bin/webpack-dev-server

  1. In config/webpacker.yml set hmr and inline dev_server properties to true.

    dev_server:
      https: false
      host: localhost
      port: 3035
      public: localhost:3035
      hmr: true
      # Inline should be set to true if using HMR
      inline: true
  2. Add react refresh packages: yarn add @pmmmwh/react-refresh-webpack-plugin react-refresh -D

  3. HMR is for use with the webpack-dev-server, so we only add this for the webpack-dev-server.

    const { devServer } = require('@rails/webpacker')
    
    const isWebpackDevServer = process.env.WEBPACK_DEV_SERVER
    
    //plugins
    if (isWebpackDevServer) {
      environment.plugins.append(
        'ReactRefreshWebpackPlugin',
        new ReactRefreshWebpackPlugin({
          overlay: {
            sockPort: devServer.port
          }
        })
      )
    }

    We added overlay.sockedPort option in ReactRefreshWebpackPlugin to match the webpack dev-server port specified in config/webpacker.yml. Thats way we make sockjs works properly and suppress error in browser console GET http://localhost:[port]/sockjs-node/info?t=[xxxxxxxxxx] 404 (Not Found).

  4. Add react-refresh plugin in babel.config.js

  module.export = function(api) {
    return {
      plugins: [process.env.WEBPACK_DEV_SERVER && 'react-refresh/babel'].filter(Boolean)
    }
  }

That's it :). Now Browser should reflect .js along with .css changes without reloading.

If by some reason plugin doesn't work you could revert changes and left only devServer hmr/inline to true affecting only css files.

These plugins are working and tested with