Hot reload client and server webpack bundles for the ultimate development experience
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


npm version npm downloads npm npm

Hot reload universally bundled webpack apps for the ultimate development experience 👏

Update 26/3/18: Now works with webpack 4 and babel 7.


If you universally bundle your app using webpack (i.e. you use webpack to bundle your server AND client side code) this package will set up hot reloading for both server and client side.

What you get from this package:

  • Automatic re-bundle on server code changes so server side rendering always reflect the latest changes.
  • Automatic re-bundle on client code changes using webpack-serve.


yarn add universal-hot-reload -D


  1. Setup your server bundle webpack config like below. The important parts are:

    • Set target to node.
    • Excluding node_modules from the server bundle by setting externals using webpack-node-externals.
    • Set libraryTarget to commonjs2 which adds module.exports to the beginning of the bundle, so universal-hot-reload can access your app.
    const path = require('path');
    const nodeExternals = require('webpack-node-externals');
    module.exports = {
      mode: 'development',
      devtool: 'source-map',
      entry: './src/server/server.js',
      target: 'node', // Important
      externals: [nodeExternals()], // Important
      output: {
        path: path.resolve('dist'),
        filename: 'serverBundle.js',
        libraryTarget: 'commonjs2' // Important
      // other standard webpack config like loaders, plugins, etc...
  2. Setup your client bundle webpack config like below. Note that in output, publicPath must be the full url to the bundle:

    const path = require('path');
    const webpackServeUrl = 'http://localhost:3002';
    module.exports = {
      mode: 'development',
      devtool: 'source-map',
      entry: './src/client/index',
      output: {
        path: path.resolve('dist'),
        publicPath: `${webpackServeUrl}/dist/`, // MUST BE FULL PATH!
        filename: 'bundle.js'
      module: {
        rules: [
            test: /\.jsx?$/,
            loader: 'babel-loader',
            include: path.resolve('src'),
            exclude: /node_modules/,
            options: {
              cacheDirectory: true,
  3. In your server bootstrap, require universal-hot-reload and invoke it, passing it your server and client webpack config objects in that order:

    const UniversalHotReload = require('universal-hot-reload').default;
  4. In your server entry file (as specified in your webpack server config "entry" property):

    • In your html template for server rendering, the script reference to the client bundle should point to webpackServeUrl/dist/bundle.js.
    • You must export your express app so universal-hot-reload can access the http.server object
    import express from 'express';
    const PORT = 3000;
    const app = express();
    app.use('/dist', express.static('dist', {maxAge: '1d'}));
    // Important: reference webpack serve url for the client bundle
    const html = `<!DOCTYPE html>
                    <div id="reactDiv">${reactString}</div>
                    <script src="http://localhost:3002/dist/bundle.js"></script>
    // Important: the listen method returns a http.server object which must be exported
    const httpServer = app.listen(PORT, () => {`Listening at ${PORT}`);
    // export http.server object so universal-hot-reload can access it
    module.exports = httpServer;
  5. Run your app!

    node src/server/index.js


Check the example for a fully working spa with react and react-router.