React Refresh (commonly known as Fast Refresh) is a system that enables near-instant feedback for edits made to React components in a development environment. It aims to preserve the state of functional components and Hooks while updating their implementation, and gracefully handles errors by retrying rendering upon code fixes.
The React Refresh architecture consists of three main parts:
react-refresh/runtime): Manages the registry of component types and coordinates updates with the React reconciler packages/react-refresh/src/ReactFreshRuntime.js1-61react-refresh/babel): Transforms source code to inject registration calls ($RefreshReg$) and Hook signatures ($RefreshSig$) packages/react-refresh/src/ReactFreshBabelPlugin.js25-27ReactFiberHotReloading to swap implementations during the render phase packages/react-reconciler/src/ReactFiberHotReloading.js64-79Title: Fast Refresh Update Cycle
Sources: packages/react-refresh/src/ReactFreshRuntime.js186-220 packages/react-refresh/src/ReactFreshBabelPlugin.js10-40 packages/react-reconciler/src/ReactFiberHotReloading.js222-235
The runtime is the central hub for component identity. It maintains mappings between "Families" (stable identities) and "Types" (the actual function/class implementations).
current property pointing to the latest implementation packages/react-reconciler/src/ReactFiberHotReloading.js38-40 packages/react-refresh/src/ReactFreshRuntime.js46ownKey (hash of hook calls) and a getCustomHooks function to track nested hook dependencies packages/react-refresh/src/ReactFreshRuntime.js22-27The runtime decides whether a component can preserve its state using canPreserveStateBetween.
haveEqualSignatures, which compares the recursive fullKey of hooks packages/react-refresh/src/ReactFreshRuntime.js121-139register(type, id): Associates a component implementation with a persistent ID. It creates a new Family if the ID is new or updates an existing one packages/react-refresh/src/ReactFreshRuntime.js247-270performReactRefresh(): The primary entry point called by the HMR (Hot Module Replacement) client. It computes the diff between old and new types, populates pendingUpdates, and triggers the reconciler update packages/react-refresh/src/ReactFreshRuntime.js186-245Sources: packages/react-refresh/src/ReactFreshRuntime.js41-75 packages/react-refresh/src/ReactFreshRuntime.js121-153 packages/react-refresh/src/ReactFreshRuntime.js78-119
The Babel plugin analyzes source files to identify "component-ish" exports and Hook calls.
The plugin identifies functions starting with a capital letter (PascalCase) or HOC calls (like memo(Foo)) and wraps them with $RefreshReg$.
Title: Babel Plugin Entity Mapping
Sources: packages/react-refresh/src/ReactFreshBabelPlugin.js42-44 packages/react-refresh/src/ReactFreshBabelPlugin.js46-209 packages/react-refresh/src/ReactFreshBabelPlugin.js211-220
To ensure Hooks remain consistent, the plugin generates a signature for every function using Hooks. This signature includes:
"useState{[foo, setFoo](#0)}\nuseEffect{}") packages/react-refresh/src/__tests__/__snapshots__/ReactFreshBabelPlugin-test.js.snap85forceReset if the signature is definitively incompatible packages/react-refresh/src/ReactFreshRuntime.js24Sources: packages/react-refresh/src/ReactFreshBabelPlugin.js25-27 packages/react-refresh/src/ReactFreshBabelPlugin.js211-220
React's Fiber reconciler is made "hot-reload aware" through the ReactFiberHotReloading module.
During the beginWork phase of the Fiber work loop, React resolves the component type. If Fast Refresh is active, it calls resolveFunctionForHotReloading.
resolveFunctionForHotReloading(type): Looks up the Family for the given type and returns family.current. This ensures that even if a parent component didn't change, its children use the latest code packages/react-reconciler/src/ReactFiberHotReloading.js64-79isCompatibleFamilyForHotReloading(fiber, element): Determines if a Fiber can be updated (re-rendered) or must be deleted and recreated (remounted). It checks if the prevType and nextType belong to the same Family packages/react-reconciler/src/ReactFiberHotReloading.js124-204Title: Reconciler Integration Logic
Sources: packages/react-reconciler/src/ReactFiberHotReloading.js64-79 packages/react-reconciler/src/ReactFiberHotReloading.js124-138 packages/react-reconciler/src/ReactFiberHotReloading.js222-235 packages/react-reconciler/src/ReactFiberHotReloading.js22-24
Fast Refresh tracks roots that failed due to errors in failedRoots packages/react-refresh/src/ReactFreshRuntime.js68 When a refresh is performed, React attempts to re-render these roots specifically via scheduleRefresh, allowing developers to fix a syntax or runtime error and see the result without a manual page refresh packages/react-reconciler/src/ReactFiberHotReloading.js206-220
Special care is taken for Activity (formerly LegacyHidden) boundaries. Errors inside a hidden Activity do not escape to the visible UI but are captured and can be retried once the boundary is revealed or updated packages/react-reconciler/src/__tests__/ActivityErrorHandling-test.js29-98 The runtime also tracks rootElements to assist in retrying failed mounts packages/react-refresh/src/ReactFreshRuntime.js73-74
Sources: packages/react-refresh/src/ReactFreshRuntime.js68 packages/react-reconciler/src/ReactFiberHotReloading.js206-220 packages/react-reconciler/src/__tests__/ActivityErrorHandling-test.js33-98 packages/react-refresh/src/ReactFreshRuntime.js73-74
The ReactFreshRuntime maintains several global collections to track the state of the application:
| Collection | Key | Value | Purpose |
|---|---|---|---|
allFamiliesByID | string (ID) | Family | Persistent storage of identities across reloads packages/react-refresh/src/ReactFreshRuntime.js46 |
allFamiliesByType | any (Type) | Family | Mapping of implementations to their identities packages/react-refresh/src/ReactFreshRuntime.js47 |
allSignaturesByType | any (Type) | Signature | Hook usage metadata for a specific function version packages/react-refresh/src/ReactFreshRuntime.js49 |
updatedFamiliesByType | any (Type) | Family | Families edited in the current session for fast lookup by reconciler packages/react-refresh/src/ReactFreshRuntime.js53 |
mountedRoots | FiberRoot | Set | Tracking active React roots to trigger updates packages/react-refresh/src/ReactFreshRuntime.js66 |
failedRoots | FiberRoot | Set | Roots that crashed and need retry on next edit packages/react-refresh/src/ReactFreshRuntime.js68 |
Sources: packages/react-refresh/src/ReactFreshRuntime.js41-75
Refresh this wiki