Mastering Webpack HMR: From Basics to Custom Module Hot Updates

This article explains the background, concepts, workflow, and hands‑on setup of Webpack’s Hot Module Replacement (HMR), detailing server‑client communication, runtime mechanics, and how to manually implement module hot‑updates without a framework, complete with code examples and configuration tips.

WeDoctor Frontend Technology
WeDoctor Frontend Technology
WeDoctor Frontend Technology
Mastering Webpack HMR: From Basics to Custom Module Hot Updates

HMR Background

When using Webpack Dev Server, developers can focus on coding because it watches file changes, recompiles, and automatically refreshes the browser. However, a full page reload loses application state, so Webpack provides Hot Module Replacement (HMR) to update modules without refreshing.

Simple Concept of HMR

HMR re‑packs changed code and sends the new modules to the browser, which replaces the old ones without a full reload, preserving state and improving development efficiency compared with traditional live reload. The following diagram illustrates the process.

Webpack Compile: watch files and write to memory.

Bundle Server: serve files to the browser.

HMR Server: output hot‑updated files to the HMR runtime.

HMR Runtime: inject files into the browser memory.

Bundle: output built files.

Getting Started with HMR

Enabling HMR is straightforward because it is built into Webpack. There are two ways:

Run webpack-dev-server with the --hot flag.

Add the following configuration to webpack.config.js:

// ./webpack.config.js
const webpack = require('webpack');
module.exports = {
  // ...
  devServer: {
    hot: true,
  },
  plugins: [
    // ...
    new webpack.HotModuleReplacementPlugin()
  ]
};

Server and Client in HMR

devServer Notifies Browser of File Changes

Webpack‑dev‑server creates a WebSocket connection using sockjs. When compilation finishes, it sends the new hash or status messages (hash, errors, warnings, ok) to the client.

sendStats(sockets, stats, force) {
  const shouldEmit =
    !force &&
    stats &&
    (!stats.errors || stats.errors.length === 0) &&
    (!stats.warnings || stats.warnings.length === 0) &&
    stats.assets &&
    stats.assets.every(asset => !asset.emitted);

  if (shouldEmit) {
    this.sockWrite(sockets, 'still-ok');
    return;
  }

  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');
  }
}

Client Responds to Server Messages

The client caches the hash and triggers a reload when it receives an ok message. An illustration:

Reload Strategy Selection

function reloadApp({ hotReload, hot, liveReload }, { isUnloading, currentHash }) {
  if (isUnloading || !hotReload) {
    return;
  }

  if (hot) {
    log.info('App hot update...');
    const hotEmitter = require('webpack/hot/emitter');
    hotEmitter.emit('webpackHotUpdate', currentHash);
    if (typeof self !== 'undefined' && self.window) {
      self.postMessage(`webpackHotUpdate${currentHash}`, '*');
    }
  } else if (liveReload) {
    let rootWindow = self;
    const intervalId = self.setInterval(() => {
      if (rootWindow.location.protocol !== 'about:') {
        applyReload(rootWindow, intervalId);
      } else {
        rootWindow = rootWindow.parent;
        if (rootWindow.parent === rootWindow) {
          applyReload(rootWindow, intervalId);
        }
      }
    });
  }
}

function applyReload(rootWindow, intervalId) {
  clearInterval(intervalId);
  log.info('App updated. Reloading...');
  rootWindow.location.reload();
}

If HMR is enabled, the client uses webpack/hot/emitter to apply the new hash; otherwise it falls back to applyReload and calls location.reload.

Webpack Requests Latest Module Code via Hash

The dev‑server listens for webpackHotUpdate, the HMR runtime checks for updates, and uses hotDownloadUpdateChunk and hotDownloadManifest to fetch updated chunks via JSONP or AJAX. The updated code is then processed by the runtime.

HMR Runtime Performs Hot Updates

The core step is hotApply, which identifies outdated modules and dependencies, removes them from the cache, and inserts the new modules.

// remove module from cache
delete installedModules[moduleId];
delete outdatedDependencies[moduleId];

// add new modules
for (moduleId in appliedUpdate) {
  if (Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {
    modules[moduleId] = appliedUpdate[moduleId];
  }
}

Hot Members in HMR

HotModuleReplacementPlugin

The plugin provides APIs such as module.hot.accept for handling updates of arbitrary JavaScript modules.

Principle of module.hot.accept

Calling module.hot.accept registers a callback in hot._acceptedDependencies. When the watched module changes, the runtime invokes the callback.

accept: function (dep, callback, errorHandler) {
  if (dep === undefined) hot._selfAccepted = true;
  else if (typeof dep === "function") hot._selfAccepted = dep;
  else if (typeof dep === "object" && dep !== null) {
    for (var i = 0; i < dep.length; i++) {
      hot._acceptedDependencies[dep[i]] = callback || function () {};
      hot._acceptedErrorHandlers[dep[i]] = errorHandler;
    }
  } else {
    hot._acceptedDependencies[dep] = callback || function () {};
    hot._acceptedErrorHandlers[dep] = errorHandler;
  }
},

Implementing JS Module Replacement

In main.js, after importing a child module, you can use module.hot.accept to replace the DOM element while preserving its state.

// ./src/main.js
import createChild from './child';

const child = createChild();
document.body.appendChild(child);
let lastChild = child;

module.hot.accept('./child', () => {
  const value = lastChild.innerHTML;
  document.body.removeChild(child);
  lastChild = createChild();
  lastChild.innerHTML = value;
  document.body.appendChild(lastChild);
});

Conclusion

This article aims to deepen understanding of HMR and help solve development scenarios such as implementing hot updates without a framework.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaScriptfrontend developmentwebpackdev server
WeDoctor Frontend Technology
Written by

WeDoctor Frontend Technology

Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.

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.