This guide walks you through migrating from the deprecated react-hot-loader to the official react-refresh-webpack-plugin (Fast Refresh).
react-hot-loaderis deprecated and no longer maintainedreact-refresh-webpack-pluginis the official solution for Fast Refresh- Better compatibility with React 18+ and webpack 5
- Active maintenance and bug fixes
- webpack 4.34+ or webpack 5
- React 16.9+ (React 17+ recommended)
- Node.js 14+
Uninstall:
npm uninstall react-hot-loader
# or
yarn remove react-hot-loaderIf using babel plugin:
npm uninstall @babel/plugin-react-hot-reload
# or
yarn remove @babel/plugin-react-hot-reloadFor webpack 5:
npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh
# or
yarn add -D @pmmmwh/react-refresh-webpack-plugin react-refreshFor webpack 4:
npm install -D @pmmmwh/react-refresh-webpack-plugin@0.4.3 react-refresh@0.9.0
# or
yarn add -D @pmmmwh/react-refresh-webpack-plugin@0.4.3 react-refresh@0.9.0// webpack.config.js
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
entry: [
'react-hot-loader/patch',
'./src/index.js',
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
plugins: ['react-hot-loader/babel'],
},
},
],
},
],
},
plugins: [
new ReactRefreshPlugin(),
],
// Disable HMR entirely if needed
// hot: false,
};// webpack.config.js
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
entry: [
'./src/index.js', // No react-hot-loader/patch needed
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
// Remove react-hot-loader/babel plugin
// Add react-refresh/babel if using Babel
plugins: ['react-refresh/babel'],
},
},
],
},
],
},
plugins: [
new ReactRefreshPlugin(),
],
// hot: true, // Keep HMR enabled for best experience
};// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// react-hot-loader setup
if (module.hot) {
module.hot.accept('./App', () => {
const NextApp = require('./App').default;
ReactDOM.render(<NextApp />, document.getElementById('root'));
});
}
ReactDOM.render(<App />, document.getElementById('root'));// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// No manual HMR setup needed - react-refresh handles it automatically
ReactDOM.render(<App />, document.getElementById('root'));react-refresh-webpack-plugin supports class components, but with limitations:
| Feature | Supported |
|---|---|
| State preservation on save | ✅ Yes |
componentDidCatch updates |
✅ Yes |
getDerivedStateFromProps |
|
shouldComponentUpdate |
✅ Yes |
PureComponent |
✅ Yes |
Note: If you experience issues with class components, consider converting them to functional components with hooks.
CRA 4+ uses react-scripts which includes react-refresh-webpack-plugin automatically.
If using react-scripts < 4:
npm install react-scripts@latest
# or
yarn upgrade react-scripts@latestVite has built-in Fast Refresh. No extra setup needed:
npm create vite@latest my-app -- --template react
# Fast Refresh works out of the box// webpack.config.js
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-typescript', '@babel/preset-react'],
plugins: ['react-refresh/babel'],
},
},
{
loader: 'ts-loader', // or fork-ts-checker-webpack-plugin
},
],
},
],
},
plugins: [
new ReactRefreshPlugin(),
],
};Cause: react-refresh package not installed.
Fix:
npm install -D react-refresh
# or
yarn add -D react-refreshCause: hot: false or HMR disabled.
Fix: Ensure HMR is enabled in webpack config:
module.exports = {
// ...
devServer: {
hot: true,
},
};Cause: Duplicate React Refresh runtime or incorrect setup.
Fix:
- Ensure only one
ReactRefreshPlugin()instance - Check for duplicate
react-refreshimports innode_modules - Clear webpack cache:
rm -rf node_modules/.cache
Cause: TypeScript's ts-loader not handling Babel plugin correctly.
Fix: Use Babel for transpilation with TypeScript:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-typescript',
['@babel/preset-react', { runtime: 'automatic' }],
],
plugins: ['react-refresh/babel'],
},
},
],
},
],
},
};Cause: React DevTools may need to reconnect.
Fix:
- Close DevTools tab
- Reload page
- Reopen DevTools
If you need to revert to react-hot-loader:
npm install react-hot-loader@latest
# Revert webpack.config.js changes
# Re-add react-hot-loader/babel plugin- Uninstall
react-hot-loader - Install
@pmmmwh/react-refresh-webpack-pluginandreact-refresh - Update webpack config (remove
react-hot-loader/patchfrom entry) - Remove manual HMR setup in entry point
- Update Babel config (remove
react-hot-loader/babel, addreact-refresh/babel) - Test Fast Refresh functionality
- Verify class components work as expected
- Update documentation for your team