Understanding React Fiber - Trees, Diff, and Commit Phases
Contents
Introduction
React Fiber is the core of modern React rendering, enabling incremental rendering, time slicing, and high-performance updates. In this blog, we will explore:
- Fiber nodes and structure
- Fiber loop and traversal
- Diff/reconciliation algorithm
- Work-in-progress (WIP) tree
- Current fiber tree vs real DOM
- Commit phase and tree flow
1. What is a Fiber Node?
A Fiber node is a unit of work in React’s incremental rendering. It contains:
type→ element type (div,span, Component)key→ unique identifier for list reconciliationprops→ element propertiesstateNode→ reference to actual DOM node or component instancechild,sibling,return→ pointers for tree traversalalternate→ link to the previous fiber (used for diffing)effectTag→ indicates updates needed (Placement,Update,Deletion)
Example fiber node:
const fiber = {
type: 'div',
key: null,
props: { className: 'container', children: [] },
stateNode: document.createElement('div'),
child: null,
sibling: null,
return: null,
alternate: currentFiber,
effectTag: 'UPDATE',
};2. Fiber Loop and Traversal
- Fiber uses an iterative loop, not recursion, to traverse the tree.
- Traversal is pre-order (parent → child → sibling).
- The loop is incremental and interruptible — React can pause work and yield control to the browser.
Pseudo-code (non-recursive traversal):
let nextFiber = wipRoot;
while (nextFiber) {
nextFiber = performUnitOfWork(nextFiber);
}
function performUnitOfWork(fiber) {
fiber.child = reconcileChildren(fiber, fiber.props.children);
if (fiber.child) return fiber.child;
let next = fiber;
while (next) {
if (next.sibling) return next.sibling;
next = next.return;
}
return null;
}3. Diff / Reconciliation Algorithm
During traversal, React performs diffing:
- Compare new element vs old fiber (alternate)
- Decide Update, Placement, Deletion
- Create or reuse fibers
- Collect effects into a linked list for commit phase
Pseudo-code for child reconciliation:
function reconcileChildren(wipFiber, elements) {
let index = 0;
let oldFiber = wipFiber.alternate && wipFiber.alternate.child;
let prevSibling = null;
while (index < elements.length || oldFiber != null) {
const element = elements[index];
let newFiber = null;
if (
element &&
oldFiber &&
element.type === oldFiber.type &&
element.key === oldFiber.key
) {
newFiber = {
...oldFiber,
props: element.props,
effectTag: 'UPDATE',
return: wipFiber,
};
} else if (element) {
newFiber = {
type: element.type,
props: element.props,
effectTag: 'PLACEMENT',
return: wipFiber,
};
} else if (oldFiber) {
oldFiber.effectTag = 'DELETION';
}
if (index === 0) wipFiber.child = newFiber;
else prevSibling.sibling = newFiber;
prevSibling = newFiber;
index++;
if (oldFiber) oldFiber = oldFiber.sibling;
}
return wipFiber.child;
}- Fiber loop drives traversal
- Diff is executed inside the loop at each fiber node
- Work-in-progress (WIP) fibers are built incrementally during traversal
4. Work-in-Progress Tree
- A parallel fiber tree separate from currentRoot
- Incrementally built during render phase
- Each fiber can reuse state from alternate
- Eventually becomes the new current tree after commit
Analogy:
- Current tree = snapshot of the UI on screen
- WIP tree = draft of the next UI
- Loop = iterating over draft, comparing with snapshot
5. Current Fiber Tree vs Real DOM
- Current fiber tree (currentRoot) = memory structure
- Real DOM = browser nodes
- Fiber nodes hold stateNode pointing to DOM nodes
- React never replaces DOM wholesale; it updates DOM according to the effect list
Summary Table:
| Concept | Current Fiber Tree | Real DOM |
|---|---|---|
| Location | Memory | Browser |
| Role | Represent committed UI | Render UI on screen |
| Mutation | None during render | Updated during commit |
| Pointers | stateNode → DOM | DOM nodes exist physically |
6. Commit Phase
Commit applies WIP tree changes to the real DOM:
- Before mutation phase → lifecycle hooks like getSnapshotBeforeUpdate
- Mutation phase → apply effect list:
- Placement → insert/move DOM
- Update → update props/text
- Deletion → remove node
- Layout phase → lifecycle hooks and useLayoutEffect
After commit:
currentRoot = wipRoot;
wipRoot = null;- DOM now reflects the committed fiber tree
- Fiber trees now mirror the updated UI
7. Overall Tree Flow
[currentRoot] ← mirror of real DOM
│
▼ diff
[work-in-progress tree] ← being built
│
▼ commit effects
[real DOM] ← updated incrementally
│
▼ update pointer
currentRoot = wipRoot- Render phase = build WIP tree + diff
- Commit phase = update DOM
- WIP tree → current tree in memory
- Real DOM updated only incrementally
8. Summary
- Fiber nodes = units of work
- Fiber loop = incremental traversal, can pause/resume
- Diff algorithm = compares old fiber vs new element
- Work-in-progress tree = draft tree built during render
- Current tree = committed fiber tree in memory
- Commit phase = apply minimal changes to real DOM
React Fiber allows efficient, interruptible, incremental UI updates, unlike the recursive virtual DOM approach in React <16.