
React Fiber Architecture
Fiber is React’s internal reconciler, rewritten from scratch in React 16. It replaced the original stack-based reconciler with an architecture that makes rendering interruptible, prioritizable, and resumable.
Why Fiber Was Needed
The original reconciler used the JavaScript call stack for traversal. Once it started reconciling a component tree, it couldn’t stop until the whole tree was processed. On large trees, this blocked the main thread for tens of milliseconds — enough to drop frames and feel janky.
Fiber moves the tree traversal into a heap-allocated linked list that React controls. This lets React:
- Pause work after processing each fiber unit
- Resume from where it left off
- Throw away in-progress work if higher-priority updates arrive
- Reuse completed work
The Fiber Node
Each React element gets a corresponding fiber node:
{
type, // 'div' | MyComponent | etc.
key,
stateNode, // DOM node for host elements, instance for class components
return, // parent fiber
child, // first child
sibling, // next sibling (forms a linked list)
pendingProps,
memoizedProps,
memoizedState, // hook state linked list (head node)
updateQueue, // pending state updates
flags, // side effect bitmask
lanes, // priority bitmask
alternate, // pointer to the other tree
} The tree is structured as a child + sibling linked list, not an array of children. This lets React traverse it iteratively with a while loop and a pointer — no call stack growth.
Two Trees: Current and Work-in-Progress
React maintains two fiber trees simultaneously:
current tree work-in-progress tree
(what's on screen) (being built)
RootFiber ────────── RootFiber (alternate)
│ │
App ──────────────── App (alternate)
│ │
Header Header (alternate) Each fiber’s alternate points to its counterpart in the other tree. When a render completes, React swaps which tree is “current” by updating a single pointer at the root. This double-buffering pattern means the current tree is never mutated mid-render.
Work Loop
React’s work loop processes one fiber at a time:
function workLoop(deadline) {
while (nextUnitOfWork && !shouldYield(deadline)) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
} shouldYield checks if the frame deadline is approaching (via scheduler package, which uses MessageChannel for sub-millisecond granularity). If yes, React yields and schedules the next chunk via postMessage.
Render Phase vs Commit Phase
Render phase (interruptible):
- Calls component functions / render methods
- Computes new fiber tree
- Marks fibers with effect flags (
Placement,Update,Deletion) - Can be paused and restarted — must be side-effect-free
Commit phase (synchronous, cannot be interrupted):
- Walks the effect list
- Mutates the DOM
- Runs
useLayoutEffectcleanup + setup - Schedules
useEffectcleanup + setup via microtask/scheduler
Priority and Lanes
Fiber introduced a priority system called “lanes”. Each update is tagged with a lane:
const SyncLane = 0b0001; // user input, onClick
const InputContinuousLane = 0b0100; // drag, scroll
const DefaultLane = 0b1000; // fetch, setTimeout
const TransitionLane = 0b10000; // startTransition React processes higher-priority lanes first. A startTransition update (low priority) gets paused if a click handler fires.
Practical Impact
- Concurrent Mode: possible only because of Fiber’s interruptible render phase
- Suspense: works by throwing a Promise — Fiber catches it, renders a fallback, retries when resolved
useEffecttiming: effects are deferred to after paint because Fiber separates render from commit- StrictMode double-invocation: Fiber renders the work-in-progress tree twice in dev to catch impure render functions









