Hot Module Replacement (HMR)

May 13, 2023#webdev

Hot Module Replacement (HMR) in web bundlers is a feature that allows you to update modules in a running application without reloading the page. This can improve your development experience by saving time and preserving the application state.

Different web bundlers may have different ways of implementing HMR, but the general idea is to exchange, add, or remove modules in a running application without reloading the page.

When HMR is enabled, the bundler watches for changes in the source code and injects updated modules into the running application, replacing the old modules. This process is done in real-time, providing an instant feedback loop for developers during development.

HMR offers several benefits:

  • Fast feedback loop: With HMR, developers can see their code changes instantly without needing to manually refresh the browser. This significantly speeds up the development process and improves productivity.
  • Preserve application state: HMR is intelligent in that it tries to preserve the application state across module updates. This means that you can make changes to the code and see the updates applied without losing the current application state or having to re-trigger certain actions.
  • Retain component state: HMR can also retain the state of components during module updates. This allows developers to focus on specific components without losing the current state of other components.
  • Improved developer experience: HMR provides a smoother development experience by eliminating the need for manual page reloads and reducing the waiting time between code changes and seeing the results.

However, it’s important to note that HMR is primarily intended for use during development and is not meant for production environments. During the production build process, the bundler will typically create optimized and minified bundles that do not include HMR functionality.

How HMR works

HMR works by employing a combination of JavaScript runtime capabilities and communication between the client and the development server. Here’s a high-level overview of how HMR typically operates:

  • Initial bundle and runtime: When the development server starts, it generates an initial bundle of the application code and serves it to the client. The client’s browser then executes this code, which includes runtime capabilities for handling HMR.
  • Dependency graph and change detection: The bundler analyzes the dependency graph of the application to determine the relationships between modules. It also sets up a mechanism to detect changes in the source code files.
  • Code change detection: As the developer makes changes to the source code, the bundler’s watcher detects those modifications. It triggers a recompilation process for the affected modules.
  • Patch generation: After recompiling the modified modules, the bundler generates a patch, which represents the difference between the new and old versions of the modules. This patch typically includes the updated code and other metadata.
  • Client update: The development server sends the patch to the client via a WebSocket or another communication channel. The client’s HMR runtime receives the patch and applies it to the running application.
  • Module replacement: The HMR runtime applies the patch to the affected modules. It replaces the old module code with the updated code, using mechanisms like JavaScript’s eval function or specific module replacement APIs.
  • Component re-rendering: Once the module code has been updated, the HMR runtime triggers a re-rendering of the affected components or re-executes the relevant parts of the application. This allows the developer to see the changes reflected immediately in the running application, without requiring a full page reload.
  • Preservation of state: The HMR runtime tries to preserve the application’s state during the module update process. It can retain the state of components, such as form input values or scroll positions, so that the developer doesn’t lose their work or need to start from scratch after each update.
  • Error handling: If there are any errors during the module update process, the HMR runtime can provide meaningful error messages and attempt to recover gracefully. It may revert to the previous working state or show an error overlay in the browser with relevant information for debugging.

The HMR process repeats as the developer continues to make changes, allowing for iterative development with instant feedback.

It’s worth noting that the implementation details of HMR can vary between different bundlers and frameworks. Webpack, for example, has its own HMR runtime and configuration options, while other bundlers may use different mechanisms to achieve similar functionality.