skip to content

In React Native, what does Fast Refresh keep when you save an edit, and when does it fall back to a full reload instead?

level: juniorimportance: must knowfreq 60%

answer

  1. dev bundles only, on by default
  2. component-only module: re-render in place
  3. other exports: re-run the importers
  4. import path from outside React: reload
  5. full reload re-runs the whole bundle

basics

~20 s

Fast Refresh swaps edited modules into the running app and keeps function component and Hook state. If an edited module is reached through an import path from outside the React tree, it falls back to a full reload, which re-runs the bundle and loses all state.

solid answer

~40 s

Fast Refresh is React Native's development-only hot update. If the edited module exports only React components, just that module re-runs and its components re-render with their local state kept. If it exports other values, that module and the modules importing it re-run. If some import path from outside the React tree reaches the edited module, for example a utility the entry file loads, Fast Refresh cannot stop safely and does a **full reload**: the whole bundle executes again from the entry file and every piece of in-memory state, including a half-filled onboarding form, starts over. Keeping component files component-only and moving shared constants into their own modules keeps most edits on the fast path.

code

tsx · 12 lines
tsx
// StepTwo.tsx: exports only a component, so edits refresh in place
import { useState } from 'react';
import { TextInput, View } from 'react-native';

export default function StepTwo() {
  const [email, setEmail] = useState('');
  return (
    <View style={{ padding: 16 }}>
      <TextInput value={email} onChangeText={setEmail} placeholder="Email" />
    </View>
  );
}

go deeper

for a junior

Recall the difference: Fast Refresh swaps code and keeps function component state, while a full reload re-runs the bundle and loses everything.

for a middle

Explain the three paths, component-only module, module with other exports, and an import path from outside React, and why the last one forces a reload.

for a senior

Show you can restructure a codebase so edits stay on the fast path: component-only files, shared constants in their own modules, no import cycles.

for a principal

Treat dev-loop speed as a team cost: module structure that defeats Fast Refresh taxes every engineer on every save.

## What Fast Refresh is **Fast Refresh** is React Native's development-time hot update. When you save a file, Metro sends the changed module to the running app over its dev-server connection, the app swaps the new code in, and React re-renders — usually within a second or two, without restarting the JavaScript. It is on by default and can be toggled with "Enable Fast Refresh" in the Dev Menu. It exists only in **development bundles**. React Native wires the React Refresh runtime in only under `__DEV__`, and React Native's Babel transformer adds the `react-refresh` Babel plugin only when a bundle is built for development with hot updates enabled — and never to files under `node_modules`. A release build has no Fast Refresh at all. A **full reload** is the other tool: the JavaScript bundle is executed again from the entry file, so every module re-initialises and all in-memory state — React state, module-level variables, stores — starts from scratch. The native side of the app keeps running. ## The three paths an edit can take Fast Refresh decides per edit, based on what the edited module **exports** and **who imports it**: | You edited… | What happens | Local component state | |---|---|---| | a module that exports only React components | that module is re-run and its components re-render | kept (function components and Hooks) | | a module with non-component exports, imported by component modules | the module and the modules importing it are re-run | kept where those components qualify | | a module that some import path reaches from outside the React tree | **full reload** | lost everywhere | The mechanism behind the table: Metro's runtime walks **up** from the edited module through its importers. A module that exports only components is a **refresh boundary** — it can absorb the update. Every import path must reach a boundary; if any path climbs all the way to the entry file (which registers the app and exports nothing) without meeting one, there is no safe place to stop, and the runtime reloads the whole app. A circular import on the path also forces a full reload. ## A worked example: the onboarding form Picture a three-step onboarding flow in which the user has typed a name and email on step 2: 1. You tweak the padding in `StepTwo.tsx`, which exports only the `StepTwo` component. Fast Refresh re-renders it; the typed text and the current step survive. 2. You change a colour in `theme.ts`, imported by the step components. `theme.ts` and the step modules re-run; the fields keep their values. 3. You edit `validation.ts`, which is also imported by a setup module that the entry file loads before rendering. That import path never passes through a component-only module, so the app **fully reloads** and the form is back on step 1, empty. The fix for case 3 is structural, as the React Native docs suggest: move shared constants and helpers into files that both sides import, and keep component files exporting only components. ## Errors do not end the session - **Syntax error**: a redbox appears; fix and save, and it disappears without a reload. - **Runtime error during module initialisation**: the redbox stays until you fix it; then the module is updated and the session continues. - **Runtime error inside a component while rendering**: after the fix, React **remounts** the app with the new code, so state below the failure is lost — unless an error boundary catches it, in which case the boundary retries rendering on the next edit. ## When to reload on purpose - The app is in a state that only initialisation code sets up (module-level caches, a store created at import time). - Fast Refresh has lost its connection to Metro — the app shows "Fast Refresh disconnected. Reload app to reconnect." - You want to be sure the behaviour you see is what a cold start produces.

  • Does Fast Refresh run in a React Native release build?
    No. The refresh runtime is installed only under `__DEV__`, and React Native's Babel transformer adds the React Refresh plugin only when a bundle is built for development with hot updates on. A release bundle contains none of it.
  • In React Native, what happens to edits made while Fast Refresh was switched off, once it is switched back on?
    React Native's HMR client stashes the updates it received while Fast Refresh was off. When you enable it again, it applies them, showing a brief refreshing banner, and then shows any compile error it ignored in the meantime.

Fast Refresh is a mechanic swapping one part while the engine keeps running; it works only if the part bolts onto something designed to take a replacement. When the part is wired straight into the ignition, the mechanic has to switch the engine off and start it again: a full reload.

saying these in an interview costs you the question

  • Fast Refresh restarts the JavaScript on every save
  • Fast Refresh also works in release builds
  • A full reload keeps React state because the native app stays running
  • Every edit refreshes in place no matter who imports the file
  • Class components keep their state just like function components