How Does Webpack 4 Hot Module Replacement Work? A Deep Dive

This article explains Webpack 4’s hot module replacement feature, covering the compilation process, dev‑server setup, entry configuration, websocket communication, the role of HotModuleReplacementPlugin, and the detailed steps of detecting changes, downloading updates via JSONP, and applying updated modules in the browser.

37 Interactive Technology Team
37 Interactive Technology Team
37 Interactive Technology Team
How Does Webpack 4 Hot Module Replacement Work? A Deep Dive

Introduction

Webpack 4 provides a powerful Hot Module Replacement (HMR) feature that updates changed modules in the browser without a full page reload. This guide walks through the entire HMR workflow, from the initial compilation to the final module replacement in the client.

1. What Is Webpack Hot Update?

Hot update allows developers to save a file (Ctrl+S) and see the changes reflected instantly in the browser, eliminating the need for manual refreshes.

2. Webpack Compilation Process

When npm run dev is executed, Webpack starts a compilation and prints a hash identifier (e.g., b6db0705ddcf5a433075) that marks the build. After a file change, a new hash (e.g., 480ff6313164fc01043e) is generated, and two additional files appear: hot-update.js and hot-update.json. The hash in these files matches the previous build, linking the two compilations.

Webpack compilation hash output
Webpack compilation hash output

3. Webpack Hot Update Mechanism

3.1 webpack-dev-server

The dev server creates a compiler instance and starts an Express server with a WebSocket layer for bidirectional communication.

// node_modules/webpack-dev-server/bin/webpack-dev-server.js
let compiler = webpack(webpackOptions);
let server = new Server(compiler, options);
server.listen(options.port, options.host, (err) => {
  if (err) throw err;
  if (options.bonjour) broadcastZeroconf(options);
  const uri = createDomain(options, server.listeningApp) + suffix;
  reportReadiness(uri, options);
});

3.2 Modifying webpack.config.js Entry

Before starting the server, server.addDevServerEntrypoints injects two client entries: the WebSocket client and the HMR runtime.

// lib/util/addDevServerEntrypoints.js
const clientEntry = `${require.resolve('../../client/')}` + `?${domain}${sockHost}${sockPath}${sockPort}`;
if (options.hotOnly) {
  hotEntry = require.resolve('webpack/hot/only-dev-server');
} else if (options.hot) {
  hotEntry = require.resolve('webpack/hot/dev-server');
}

Resulting entry configuration:

{
  entry: {
    main: [
      'xxx/node_modules/webpack-dev-server/client/index.js?http://localhost:8080',
      'xxx/node_modules/webpack/hot/dev-server.js',
      './src/main.js'
    ]
  }
}

3.3 Listening for Compilation Completion

Webpack‑dev‑server taps into the compiler’s done hook and pushes the latest hash and any errors/warnings to connected browsers via WebSocket.

done.tap('webpack-dev-server', (stats) => {
  this._sendStats(this.sockets, this.getStats(stats));
  this._stats = stats;
});

_sendStats() {
  this.sockWrite(sockets, 'hash', stats.hash);
  if (stats.errors.length > 0) {
    this.sockWrite(sockets, 'errors', stats.errors);
  } else if (stats.warnings.length > 0) {
    this.sockWrite(sockets, 'warnings', stats.warnings);
  } else {
    this.sockWrite(sockets, 'ok');
  }
}

3.4 File Watching

The server uses webpack-dev-middleware, which internally calls compiler.watch to monitor source files. Compiled assets are written to an in‑memory filesystem for fast access.

// node_modules/webpack-dev-middleware/index.js
compiler.watch(options.watchOptions, (err) => { /* error handling */ });
setFs(context, compiler); // writes bundles to memory‑fs

3.5 Browser Receiving Update Notifications

The injected client script ( webpack-dev-server/client/index.js) opens a WebSocket connection and registers handlers for hash and ok events.

// webpack-dev-server/client/index.js
var socket = require('./socket');
var onSocketMessage = {
  hash: function(_hash) { status.currentHash = _hash; },
  ok: function() { sendMessage('Ok'); reloadApp(options, status); }
};
socket(socketUrl, onSocketMessage);

When the ok event fires, reloadApp checks module.hot and emits a webpackHotUpdate event.

// webpack-dev-server/client/util/reloadApp.js
function reloadApp() {
  if (hot) {
    log.info('[WDS] App hot update...');
    var hotEmitter = require('webpack/hot/emitter');
    hotEmitter.emit('webpackHotUpdate', currentHash);
  }
}

3.6 HotModuleReplacementPlugin

The plugin injects runtime code that defines module.hot.check and handles the update lifecycle. It adds a hotCreateModule wrapper to each module when HMR is enabled.

3.7 module.hot.check Workflow

Requests hash-update.json via hotDownloadManifest using the previous hash.

Receives a list of changed modules and the next hash, then starts the preparation phase.

Subsequent JSONP request fetches hash.hot-update.js containing the new module code.

function hotDownloadUpdateChunk(chunkId) {
  var head = document.getElementsByTagName('head')[0];
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.charset = 'utf-8';
  script.src = __webpack_require__.p + '' + chunkId + '.' + hotCurrentHash + '.hot-update.js';
  head.appendChild(script);
}

3.8 Applying the Update ( hotApply )

The core of HMR is the hotApply function, which removes outdated modules, inserts the new ones, and re‑executes the updated code.

var queue = outdatedModules.slice();
while (queue.length > 0) {
  moduleId = queue.pop();
  module = installedModules[moduleId];
  delete outdatedDependencies[moduleId];
  outdatedSelfAcceptedModules.push({ module: moduleId });
}
appliedUpdate[moduleId] = hotUpdate[moduleId];
for (moduleId in appliedUpdate) {
  if (Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {
    modules[moduleId] = appliedUpdate[moduleId];
  }
}
for (i = 0; i < outdatedSelfAcceptedModules.length; i++) {
  var item = outdatedSelfAcceptedModules[i];
  moduleId = item.module;
  try {
    __webpack_require__(moduleId);
  } catch (err) {
    // error handling
  }
}

4. Summary

Webpack’s HMR works by tracking a compilation hash, notifying the browser via WebSocket, downloading updated chunks with JSONP, and finally swapping modules in place without a full reload. Understanding each step—from dev‑server initialization to the hotApply algorithm—helps developers debug issues and customize the update flow.

HMR summary diagram
HMR summary diagram
JavaScriptWebpackdev serverHot Module ReplacementFrontend Build
37 Interactive Technology Team
Written by

37 Interactive Technology Team

37 Interactive Technology Center

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.