skip to content

Fast Refresh

Fast Refresh swaps edited modules into the running app, keeping function component and Hook state but remounting or fully reloading in some cases. Interviewers ask why state vanished after an edit.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

4

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
open as a page

In a React Native onboarding form, why does typed input survive some Fast Refresh edits but reset on others?

level: middleimportance: must knowfreq 48%

basics

~20 s

Fast Refresh keeps state only for function components and Hooks it can match to the previous version. Class components, files with extra non-component exports, changed Hook arguments or order, a render error and // @refresh reset all cause a remount, which resets the typed input.

open as a page

During a React Native Fast Refresh update, why does a useEffect with an empty dependency array run again, and how should it be written?

level: middleimportance: should knowfreq 34%

basics

~20 s

During Fast Refresh, Hooks with dependencies always update and their dependency lists are ignored, so even a useEffect with an empty array runs its cleanup and then its body again. Effects should clean up fully and be safe to re-run.

open as a page

In a React Native app where every save causes a full reload instead of a Fast Refresh, how do you find and fix the cause?

level: seniorimportance: should knowfreq 26%

basics

~20 s

Fast Refresh walks up the edited module's importers looking for component-only modules. It reloads fully when a path reaches the entry file without one, when it meets an import cycle, or when a boundary's exports change. Fix the import graph, not Fast Refresh.

open as a page