How does React Fiber work?
The Short Answer
React Fiber is the internal reconciliation engine introduced in React 16 that replaced the old 'stack reconciler.' The key innovation is that Fiber makes rendering interruptible — instead of processing the entire component tree in one synchronous pass (which blocks the main thread), Fiber breaks work into small units called 'fibers' that can be paused, resumed, or aborted. This enables features like concurrent rendering, Suspense, and transitions. Each fiber is a JavaScript object representing a unit of work — essentially a node in a linked-list tree that React walks through incrementally, yielding back to the browser between chunks to keep the UI responsive.
The Problem Fiber Solves
Before Fiber (React 15 and earlier), reconciliation was synchronous and recursive. When state changed, React would diff the entire subtree in one go — if you had a tree with 10,000 components, it would process all of them before the browser could paint a single frame. For large updates, this meant dropped frames, janky animations, and unresponsive inputs. The old reconciler couldn't pause mid-tree because it used the JavaScript call stack (recursive function calls), and you can't pause a call stack.
Stack vs Fiber — The Analogy
The old reconciler is like reading a book cover-to-cover without stopping — if someone asks you a question, they have to wait until you finish the whole book. Fiber is like using a bookmark — you can stop at any page, answer the question (handle a user event), then pick up where you left off. The bookmark (fiber node) remembers your place.
What a Fiber Node Is
A fiber is a plain JavaScript object that represents a component instance, a DOM element, or other React node. It contains information about the component type, its props and state, its position in the tree, and pointers to related fibers. Unlike the recursive call stack, fibers form a linked-list structure that React can traverse iteratively — meaning it can stop at any point and resume later without losing context.
// Simplified representation of a Fiber node
// (actual React source has ~30 fields)
type Fiber = {
// Identity
tag: number; // FunctionComponent, ClassComponent, HostComponent, etc.
type: any; // The component function/class, or 'div', 'span', etc.
key: string | null; // Reconciliation key
// Tree structure (linked list, not array children)
return: Fiber | null; // Parent fiber
child: Fiber | null; // First child fiber
sibling: Fiber | null; // Next sibling fiber
index: number; // Position among siblings
// State
memoizedProps: any; // Props from last render
memoizedState: any; // State from last render (linked list of hooks)
pendingProps: any; // New props to process
// Effects
flags: number; // Bitfield: Placement, Update, Deletion, etc.
subtreeFlags: number; // Aggregated flags from children
// Work scheduling
lanes: number; // Priority lanes for this fiber's pending work
childLanes: number; // Priority lanes for children's pending work
// Double buffering
alternate: Fiber | null; // The other version (current ↔ workInProgress)
};
// Tree traversal is iterative, not recursive:
// 1. Process current fiber
// 2. If it has a child → move to child
// 3. If no child → move to sibling
// 4. If no sibling → move to parent's sibling (uncle)
// 5. Repeat until back at root
// At any step, React can PAUSE and yield to the browser
Two-Phase Rendering
Fiber splits rendering into two distinct phases. The render phase (also called reconciliation) is where React figures out what changed — it walks the fiber tree, calls component functions, diffs props, and builds a list of effects (DOM mutations needed). This phase is interruptible and has no visible side effects. The commit phase applies all the effects to the actual DOM in one synchronous batch — this phase is NOT interruptible because partial DOM updates would look broken to the user.
- Render Phase (interruptible):
- Walk the fiber tree, calling component functions
- Diff old vs new props/state
- Mark fibers with effect flags (needs update, needs insertion, needs deletion)
- Can be paused, resumed, or restarted — no visible changes yet
- Commit Phase (synchronous, not interruptible):
- Apply all DOM mutations in one batch
- Run useLayoutEffect callbacks
- Update refs
- Schedule useEffect callbacks (run after paint)
Double Buffering
React maintains two fiber trees at all times: the 'current' tree (what's on screen) and the 'workInProgress' tree (what's being built for the next render). Each fiber has an alternate pointer to its counterpart in the other tree. During the render phase, React builds the workInProgress tree by cloning and updating fibers from the current tree. Once the commit phase finishes, the workInProgress tree becomes the new current tree — a simple pointer swap. This is called double buffering, borrowed from graphics programming.
// Conceptual model of double buffering:
// Before render:
// current tree: A → B → C (what's on screen)
// workInProgress: null
// During render phase:
// current tree: A → B → C (still on screen, unchanged)
// workInProgress: A' → B' → C' (being built, may differ)
// A.alternate = A', A'.alternate = A
// After commit:
// current tree: A' → B' → C' (now on screen — pointer swap)
// workInProgress: A → B → C (becomes the next workInProgress base)
// Why this matters:
// 1. If render is interrupted/aborted, current tree is untouched
// 2. React can compare current vs workInProgress to find minimal changes
// 3. The old tree is reused as the base for the next workInProgress (less allocation)
// This is why you can call setState during render in concurrent mode
// and React just restarts the workInProgress — no DOM corruption
Priority and Lanes
Fiber introduced the concept of work priorities (now called 'lanes'). Not all updates are equally urgent — a user typing in an input is more important than a background data fetch updating a list. Lanes are a bitfield system where each bit represents a priority level. React can interrupt low-priority work to handle high-priority updates first, then resume the low-priority work later. This is what powers useTransition and useDeferredValue.
| Lane | Priority | Example |
|---|---|---|
| SyncLane | Highest — must be synchronous | Controlled input onChange, focus management |
| InputContinuousLane | High — user is actively interacting | Drag events, scroll handlers |
| DefaultLane | Normal — standard updates | setState from event handlers, data fetches |
| TransitionLane | Low — can be interrupted | useTransition updates, navigation |
| IdleLane | Lowest — do when nothing else is pending | Offscreen pre-rendering, analytics |
When a high-priority update arrives while React is processing a low-priority render, Fiber can abandon the in-progress workInProgress tree, process the urgent update first (creating a new workInProgress), commit it, then restart the low-priority work. The user sees the urgent update immediately without waiting for the large low-priority tree to finish.
Features Enabled by Fiber
Fiber isn't a feature you use directly — it's the architecture that makes modern React features possible. Without interruptible rendering and priority scheduling, none of these would work correctly.
- Concurrent rendering — multiple versions of UI in memory simultaneously
- Suspense — pause rendering while data loads, show fallback, resume when ready
- useTransition — mark updates as non-urgent so they don't block user input
- useDeferredValue — show stale value while fresh value renders in background
- Automatic batching — group multiple setState calls into one render
- Streaming SSR — send HTML in chunks as components resolve on the server
- Offscreen rendering — pre-render content that's not yet visible
Why Interviewers Ask This
React Fiber is a hard question that separates surface-level React users from developers who understand the framework's internals. Interviewers want to see that you know why the old stack reconciler was problematic (synchronous, blocking), can explain what 'interruptible rendering' means concretely (linked-list traversal vs recursive call stack), understand the two-phase model (render vs commit) and why commit must be synchronous, know how priorities/lanes enable concurrent features, and can connect Fiber to user-facing APIs like Suspense and useTransition. You don't need to know every field on a fiber node, but you should understand the architecture and its implications.
Quick Revision Cheat Sheet
What Fiber is: React's reconciliation engine — makes rendering interruptible via linked-list traversal
Fiber node: JS object representing a unit of work — has child/sibling/return pointers
Two phases: Render (interruptible, finds changes) → Commit (synchronous, applies DOM mutations)
Double buffering: Two trees: current (on screen) and workInProgress (being built)
Lanes: Priority system — urgent updates interrupt low-priority renders
Enables: Concurrent rendering, Suspense, useTransition, automatic batching, streaming SSR