Webpack Hot Middleware
Webpack Hot Middleware provides hot module replacement (HMR) capabilities for Webpack applications by integrating with an existing server, such as Express, and `webpack-dev-middleware`. This library is a direct alternative to `webpack-dev-server` for scenarios where developers need more control over their server setup, particularly for integrating HMR into custom backend environments. It focuses exclusively on the client-server communication mechanism for HMR updates, subscribing to server-side changes and executing them using Webpack's native HMR API. The current stable version is 2.26.1, released in February 2024, indicating a steady but infrequent release cadence primarily for bug fixes and compatibility updates. It does not handle the actual component-level hot reloading, leaving that to other libraries like React Hot Loader.
Common errors
-
Hot Module Replacement is disabled.
cause The `webpack.HotModuleReplacementPlugin` was not added to the webpack configuration's `plugins` array.fixAdd `new webpack.HotModuleReplacementPlugin()` to your `webpack.config.js`. -
Module not found: Error: Can't resolve 'webpack-hot-middleware/client'
cause The `webpack-hot-middleware/client` entry point string is incorrect or misspelled in your webpack configuration.fixVerify the entry string `webpack-hot-middleware/client` (including the exact casing) in your `webpack.config.js`. -
WebSocket connection to 'ws://localhost:3000/__webpack_hmr' failed: Error during WebSocket handshake: Unexpected response code: 404 (or similar connection errors in browser console)
cause The `webpack-hot-middleware` is not correctly mounted on your server, or the `path` configuration option (both server-side and client-side via query string) does not match.fixEnsure `app.use(webpackHotMiddleware(compiler, { path: '/__webpack_hmr' }));` is correctly set up on your server and that the client entry also specifies the same path: `webpack-hot-middleware/client?path=/__webpack_hmr`. -
compiler.plugin is not a function (or similar errors related to compiler hooks)
cause `webpack-hot-middleware` might be using deprecated Webpack compiler hooks in an older version, or you're using a version incompatible with your Webpack major version.fixEnsure `webpack-hot-middleware` is updated to a version compatible with your installed Webpack version (e.g., v2.25.3+ for better Webpack 5 compatibility).
Warnings
- breaking As of version 2.0.0, all client functionality was rolled directly into `webpack-hot-middleware`. You must remove any references to `webpack/hot/dev-server` or `webpack/hot/only-dev-server` from your webpack configuration's entry array.
- gotcha `webpack-hot-middleware` requires `webpack-dev-middleware` to be installed and correctly configured to serve the webpack-compiled assets. It works in conjunction with `webpack-dev-middleware`, not as a standalone solution for asset serving.
- gotcha The `webpack.HotModuleReplacementPlugin` must be included in your webpack configuration's `plugins` array for Hot Module Replacement to function. Without this plugin, `webpack-hot-middleware` will not be able to enable HMR.
- gotcha For correct HMR client behavior, the `'webpack-hot-middleware/client'` entry should generally be the *first* item in your `entry` array for each bundle. This ensures the HMR client loads before application code, allowing it to correctly intercept and handle updates.
- gotcha When using multiple Webpack entry points, `webpack-hot-middleware/client` must be included in *each* entry point that you want to enable hot reloading for. Omitting it from an entry point will prevent HMR for that specific bundle.
- gotcha To correctly apply HMR updates to your application code (e.g., React components, style sheets), you must explicitly use Webpack's HMR API (`module.hot.accept()`) in your client-side code, or use a loader/plugin that does so automatically (like `react-hot-loader` or `style-loader`). `webpack-hot-middleware` only provides the transport layer.
Install
-
npm install webpack-hot-middleware -
yarn add webpack-hot-middleware -
pnpm add webpack-hot-middleware
Imports
- webpackHotMiddleware
import webpackHotMiddleware from 'webpack-hot-middleware'; // or import { webpackHotMiddleware } from 'webpack-hot-middleware';const webpackHotMiddleware = require('webpack-hot-middleware'); - 'webpack-hot-middleware/client'
entry: { main: ['./src/main.js', 'webpack-hot-middleware/client'] }entry: { main: ['webpack-hot-middleware/client', './src/main.js'] } - HotModuleReplacementPlugin
new HotModuleReplacementPlugin()
new webpack.HotModuleReplacementPlugin()
Quickstart
/* webpack.config.js */
const webpack = require('webpack');
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: ['webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000', './src/main.js'],
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
// new webpack.NoEmitOnErrorsPlugin() // Optional: Uncomment for cleaner error handling, but may hide issues
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: { presets: ['@babel/preset-env'] }
}
}
]
}
};
/* server.js */
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackConfig = require('./webpack.config');
const app = express();
const compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler, {
publicPath: webpackConfig.output.publicPath,
stats: { colors: true },
}));
app.use(webpackHotMiddleware(compiler, {
log: console.log,
path: '/__webpack_hmr',
heartbeat: 10 * 1000,
}));
app.get('/', (req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head><title>Webpack Hot Middleware</title></head>
<body>
<h1>Hello Webpack Hot Middleware!</h1>
<div id="root"></div>
<script src="/main.bundle.js"></script>
</body>
</html>
`);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});
/* src/main.js */
import './styles.css'; // Assume you have a simple css file
console.log('App started!');
const root = document.getElementById('root');
let count = 0;
function render() {
root.innerHTML = `Count: ${count++}`;
}
render();
if (module.hot) {
module.hot.accept('./main.js', function() {
console.log('Accepting updated main.js!');
render();
});
module.hot.accept('./styles.css', function() {
console.log('Accepting updated styles.css!');
});
}