pmmmwh/react-refresh-webpack-plugin: Fast Refresh for React on Webpack 5
A Webpack plugin to enable "Fast Refresh" (also previously known as Hot Reloading) for React components.
At a glance
- What is it?
- The plugin that wires React Fast Refresh into Webpack 5 through babel-loader or ts-loader. It is a development-only tool with hard peer dependency floors, and the README still labels it experimental.
- Who is it for?
- Adopt it if you are running Webpack 5 with react 16.9.0 or newer and you want component state preserved across edits without switching bundlers. Do not adopt it if you are on Vite, on Webpack 4, or if you need a stable, non-experimental dependency surface.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What React Fast Refresh on Webpack actually fixes
Editing a React component in a Webpack dev server and losing all component state on every save is the problem this plugin exists to remove. Webpack's Hot Module Replacement can swap a module, but it has no idea what a React component is. React Fast Refresh is the React team's protocol for accepting a swapped module and re-rendering the component tree while preserving hook state. The plugin is the bridge: it registers the runtime, connects the dev server's update channel to the React refresh runtime, and renders the error overlay when a transform fails.
It is for teams already committed to Webpack 5 who want the same edit experience that newer bundlers ship by default. It is not a bundler. It does not replace Webpack, and it does not work on Webpack 4, because the minimum supported webpack version listed in the README is 5.2.0.
The package description in package.json still calls it an EXPERIMENTAL Webpack plugin, and the README asks for issue reports ahead of a v1 release. That framing has been there through the 0.6.x line, so treat the API as stable in practice but not contractually frozen.
How the Babel transform and the plugin split the work
Fast Refresh is two halves that must both be present. The first half is a compile-time transform: react-refresh/babel rewrites each component so the runtime can identify it and register a refresh boundary. The second half is the runtime plugin, which injects the refresh runtime into the bundle and hooks into Webpack's HMR lifecycle. Enable one without the other and you get a build that looks fine and refreshes nothing useful.
The README is explicit that both must be enabled only in development mode. That is why every example uses `isDevelopment && ...` inside a `.filter(Boolean)` array rather than a plain plugin entry: the same config file serves both modes, and the conditional is what keeps the transform out of production.
The repository layout reflects the split. The transform-side loader lives under loader/, the browser runtime under client/, the error overlay under overlay/, and the dev-server communication under sockets/. Options are validated from options/, and the published types come from types/. The repository also ships examples/ for webpack-dev-server, webpack-hot-middleware and webpack-plugin-serve, which is the clearest signal of which dev servers are actually supported.
Installing the plugin and getting a first refresh
Install the plugin and its peer dependency together. react-refresh comes from the React team and is required; the README recommends 0.10.0 or above.
npm install -D @pmmmwh/react-refresh-webpack-plugin react-refreshIf you use yarn or pnpm, the README gives the equivalents as `yarn add -D @pmmmwh/react-refresh-webpack-plugin react-refresh` and `pnpm add -D @pmmmwh/react-refresh-webpack-plugin react-refresh`.
Next, make sure your development config has HMR turned on. With webpack-dev-server the README's example sets `hot: true` under devServer.
const isDevelopment = process.env.NODE_ENV !== 'production';
module.exports = {
mode: isDevelopment ? 'development' : 'production',
devServer: {
hot: true,
},
};Then wire the Babel transform and the plugin into the same config, both gated on the same flag.
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const isDevelopment = process.env.NODE_ENV !== 'production';
module.exports = {
mode: isDevelopment ? 'development' : 'production',
module: {
rules: [
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve('babel-loader'),
options: {
plugins: [isDevelopment && require.resolve('react-refresh/babel')].filter(Boolean),
},
},
],
},
],
},
plugins: [isDevelopment && new ReactRefreshWebpackPlugin()].filter(Boolean),
};Start the dev server and edit a component that holds state, for example a counter. The count should survive the save. If the whole page reloads instead, the transform and the plugin are out of sync: check that both conditionals resolve to the same value in the running process.
If you compile TypeScript with ts-loader rather than babel-loader, the README points to react-refresh-typescript and requires TypeScript 4.0 or above. It labels that path un-official and community maintained, so it is not covered by the same support expectation as the babel-loader route.
TypeScript users on `webpack.config.ts` get types out of the box, but the exported types depend on type-fest, which must be added as a devDependency. The README notes that type-fest 4.x requires Node.js 16 or above, 3.x requires 14.16 or above, and 2.x requires 12.20 or above.
The peer dependency floors are not negotiable
The README's prerequisite table is the part most likely to be skimmed and then hit. Node.js 18.12.0 or above, react and react-dom at 16.13.0 or above (the minimum table allows 16.9.0), react-refresh at 0.10.0 or above, and webpack at 5.2.0 or above. The README states plainly why: older versions do not contain the code needed to orchestrate Fast Refresh, so they cannot be made compatible by configuration.
That has a practical consequence. If you are maintaining a Webpack 4 build, this plugin is not an upgrade you can stage incrementally. The Webpack 5 migration comes first, and only then does this become available. On a monorepo with mixed Webpack versions, the plugin belongs only in packages that have already moved.
The custom renderer caveat is the second trap. For components rendered by react-three-fiber, react-pdf or ink, the README says the renderer must depend on a recent react-reconciler, recommending 0.25.0 or above and stating that anything above 0.22.0 should work. If the renderer is not compatible, the README directs you to file an issue with that renderer, not here. That is a real boundary: the plugin cannot patch a reconciler it does not control.
Where this plugin is the wrong choice
The clearest case is a project on Vite. Vite's React plugin handles Fast Refresh internally, and adding this plugin would mean running two bundlers for one job. If your team has already moved, there is nothing here to adopt.
The second case is production. This is a development-time plugin. The README repeats the instruction to enable both the Babel transform and the plugin only in development mode. A config that leaks either one into a production build is a bug in your config, not a limitation of the plugin, but the plugin does not defend you against it.
The third case is anyone expecting the error overlay or the refresh runtime to work outside a supported dev server. The examples cover webpack-dev-server, webpack-hot-middleware and webpack-plugin-serve. A custom dev server that does not expose an HMR channel compatible with the sockets/ layer is outside what the repository demonstrates.
Finally, if you need a dependency with a stable 1.0 contract, this is not it yet. The README is still asking for issue reports ahead of v1, and package.json still carries the EXPERIMENTAL label.
Compared with react-hot-loader and with Vite's built-in refresh
react-hot-loader is the predecessor approach, and the difference is architectural rather than cosmetic. It worked by wrapping components and patching React internals from the outside, which is why it accumulated configuration and edge cases. Fast Refresh was built into React itself, and this plugin talks to that built-in protocol instead of monkey-patching around it. If you are migrating off react-hot-loader, you are removing a wrapper layer, not swapping one for another.
Against Vite, the difference is where the work happens. Vite serves native ES modules in development and applies Fast Refresh through its own React plugin; there is no Webpack in the loop. This plugin assumes Webpack 5 and integrates with Webpack's module graph and HMR runtime. That means it inherits Webpack's loader ecosystem, which matters if your build already depends on loaders with no Vite equivalent, and it inherits Webpack's dev-server startup cost, which is the trade you are making.
There is also react-refresh-typescript for ts-loader users, which the README treats as a community integration rather than a first-class path. If your TypeScript build does not use Babel, that is the piece you are actually depending on.
Licence and the cost of keeping it current
The plugin is MIT licensed, as stated in the LICENSE file and the package metadata. MIT is permissive: it allows commercial use, modification and redistribution, and it requires that the copyright notice and licence text be preserved. This is not legal advice, and the react-refresh peer dependency carries its own licence from the React team, so check both before shipping.
The maintenance picture from the repository data is straightforward. The repository is not archived, and the last push was on 2026-09-10, which is recent. The release cadence is slower than the commit cadence: v0.6.3 landed on 2026-08-27, v0.6.2 on 2025-11-26, and v0.6.1 on 2025-06-25. So you should expect fixes to appear on main before they appear in a tagged version.
Upgrade cost is mostly driven by the peer floors rather than by the plugin's own API. Bumping Node.js or webpack can force a plugin bump, and the type-fest dependency means TypeScript consumers have a second version constraint to track. The README does not document a rollback procedure, so pinning a known-good version in your lockfile is the only recovery path the repository describes.
Editorial conclusion
Adopt it if you are running Webpack 5 with react 16.9.0 or newer and you want component state preserved across edits without switching bundlers. Do not adopt it if you are on Vite, on Webpack 4, or if you need a stable, non-experimental dependency surface. Before wiring it in, verify three things: that your webpack version is 5.2.0 or above, that react-refresh is installed at 0.10.0 or above, and that both react-refresh/babel and ReactRefreshWebpackPlugin are gated behind the same isDevelopment flag so neither one reaches a production bundle. If you use ts-loader instead of babel-loader, note that the README calls that integration un-official and community maintained, so it is the piece most likely to break on a toolchain upgrade.
Frequently asked questions
Does anyone still use webpack?
The README's own prerequisites require webpack 5.2.0 or above, and the repository ships examples for webpack-dev-server, webpack-hot-middleware and webpack-plugin-serve, so Webpack remains the assumed build tool for anyone using this plugin.
What is the purpose of webpack?
This plugin does not define Webpack; it plugs into Webpack's Hot Module Replacement lifecycle so that React Fast Refresh can preserve component state across edits. The plugin injects the refresh runtime and hooks into the HMR channel rather than replacing the bundler.
Does React use webpack or Vite?
The plugin assumes Webpack 5 and integrates with its module graph and HMR runtime. Vite applies Fast Refresh through its own React plugin, so a project on Vite would not add this plugin at all.
How do I refresh a React page?
For component-level refreshes, the README's setup pairs the react-refresh/babel transform with ReactRefreshWebpackPlugin and enables HMR, for example `hot: true` under devServer in webpack-dev-server. Both the transform and the plugin must be enabled only in development mode.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pmmmwh-react-refresh-webpack-plugin)