React Refresh
React Refresh is the core package that implements Fast Refresh, React's official hot reloading mechanism designed for a superior development experience. This feature allows developers to instantly see changes to React components without losing their local state, significantly speeding up the feedback loop during development. It achieves this by updating only the necessary component code and re-rendering, preserving state for functional components and Hooks. The `react-refresh` package itself, currently at version 0.18.0 (last published in October 2025), provides the underlying runtime and Babel transform. It is primarily consumed by bundler-specific plugins, such as `@pmmmwh/react-refresh-webpack-plugin` for Webpack or `@vitejs/plugin-react` for Vite, rather than being imported directly into application code. It maintains an active release cadence, with updates demonstrating sustainable maintenance. Fast Refresh differentiates itself from older hot-reloading solutions by being officially supported by the React team, offering greater reliability, and prioritizing safe state preservation.
Common errors
-
Module not found: Error: You attempted to import /.../node_modules/react-refresh/runtime.js which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.
cause This typically occurs when `react-refresh/runtime.js` is being directly imported or referenced in application code, or when the build setup (e.g., Webpack or Vite configuration) is incorrectly trying to bundle it as part of the application source.fixEnsure `react-refresh/runtime.js` is not directly imported in your application code. This module is intended to be injected and managed by bundler plugins during development. Verify your bundler configuration is set up correctly for Fast Refresh, and that `react-refresh` is configured as a `devDependencies` (or `peerDependencies`) and handled by build tools. If using Create React App, ensure it's up to date and that `FAST_REFRESH=false` is not unintentionally set. -
Error: Hot Module Replacement (HMR) is not enabled! React-refresh requires HMR to function properly.
cause Fast Refresh fundamentally relies on a Hot Module Replacement (HMR) mechanism provided by the underlying bundler (Webpack, Vite, etc.). This error indicates that HMR is not correctly configured or enabled in the development server setup.fixVerify that HMR is explicitly enabled in your bundler's development server configuration (e.g., `devServer.hot: true` in Webpack or Vite's default HMR settings). Ensure the necessary HMR plugins or settings are active in development mode. -
Warning: Invalid hook call. Hooks can only be called inside of the body of a function component.
cause While not exclusively a `react-refresh` error, this warning can sometimes appear when there are multiple or conflicting versions of React in the `node_modules` tree, which can be exacerbated by issues with Fast Refresh setup or dependency resolution in complex projects or monorepos.fixInspect your dependency tree using `npm ls react` or `yarn why react` to identify duplicate or conflicting React versions. Use `npm dedupe` or `yarn resolutions` in `package.json` to force a single, consistent React version across your project. Ensure you are using the latest compatible versions of React, React DOM, and all related Fast Refresh plugins. -
Uncaught ReferenceError: $RefreshReg$ is not defined.
cause This error occurs when the `react-refresh/babel` transform processes code, expecting the global `$RefreshReg$` and `$RefreshSig$` functions to be available, but they have not been injected by the bundler's runtime. This is common in indirect code paths like Web Workers or JS templates where the standard Fast Refresh runtime injection doesn't happen.fixFor code paths that require Babel transformation with `react-refresh/babel` but are not part of the main application bundle (and thus miss the runtime injection), manually 'polyfill' these globals at the entry point of that specific code path (e.g., `self.$RefreshReg$ = () => {}; self.$RefreshSig$ = () => () => {};`). Additionally, ensure any components or variables in such indirect paths that don't render React are not named in PascalCase, to prevent the Babel plugin from processing them unnecessarily.
Warnings
- gotcha Fast Refresh does not preserve local state for React class components; it only works reliably with functional components and Hooks. Class components will typically remount on changes, losing their state.
- gotcha Files that export both React components and non-React values (e.g., constants, utility functions) may cause Fast Refresh to fall back to a full page reload or re-run more modules than necessary. Similarly, un-named/anonymous or non-PascalCased React components can prevent Fast Refresh from working optimally.
- breaking React Refresh and its associated bundler plugins (like `@pmmmwh/react-refresh-webpack-plugin` or `@vitejs/plugin-react`) are designed exclusively for development environments. Enabling them in production builds can introduce vulnerabilities, unnecessary code, and performance overhead.
- gotcha Version mismatches between `react-refresh` and its bundler-specific integration plugins (e.g., `@pmmmwh/react-refresh-webpack-plugin`) can lead to Fast Refresh failures, broken HMR, or unexpected behavior.
- gotcha Using the `// @refresh reset` comment anywhere in a file will force components defined in that file to be completely remounted on every edit, resetting their state, even if Fast Refresh would normally preserve it. This is an intentional override but can lead to unexpected state loss if used inadvertently.
Install
-
npm install react-refresh -
yarn add react-refresh -
pnpm add react-refresh
Imports
- react-refresh/babel
plugins: ['react-refresh/babel'] // in Babel config or babel-loader options
- $RefreshReg$, $RefreshSig$
import {$RefreshReg$, $RefreshSig$} from 'react-refresh/runtime'// Handled by bundler plugins, e.g., injected preamble. Not for direct app import.
- ReactRefreshWebpackPlugin
import { ReactRefreshWebpackPlugin } from 'react-refresh'import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'
- react
import reactRefresh from '@vitejs/plugin-react-refresh'
import react from '@vitejs/plugin-react'
Quickstart
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// vite.config.ts or vite.config.js
export default defineConfig({
plugins: [
react({
// The `@vitejs/plugin-react` plugin automatically enables Fast Refresh
// (Hot Module Replacement) for React components by default in development mode.
// It leverages 'react-refresh' internally. No explicit import of 'react-refresh'
// or manual Fast Refresh configuration is typically needed directly in the app.
// 'jsxRuntime: "automatic"' is a common modern setting for React 17+.
jsxRuntime: 'automatic',
// 'fastRefresh: true' is the default for development mode in this plugin.
// You generally don't need to explicitly set it unless overriding.
fastRefresh: process.env.NODE_ENV !== 'production',
}),
],
// Ensure the Vite development server is configured for HMR (this is the default behavior).
server: {
hmr: true,
},
// For TypeScript, if you're using it, ensure your tsconfig.json has 'jsx: "react-jsx"' or 'jsx: "react-jsxdev"'.
// For example, in tsconfig.json:
// {
// "compilerOptions": {
// "jsx": "react-jsxdev"
// }
// }
});