React Virtual DOM & Reconciliation
What is the Virtual DOM?
The Virtual DOM is a lightweight JavaScript object tree that mirrors the structure of the real DOM. React keeps two versions: the current tree and the work-in-progress tree.
// JSX like this:
<div className="card">
<h1>{title}</h1>
<p>{body}</p>
</div>
// Becomes a plain JS object: { type: 'div', props: { className: 'card' }, children: [ { type: 'h1', props: {}, children: [title] }, { type: 'p', props: {}, children: [body] } ] }
The Virtual DOM's value: comparing two JS objects is orders of magnitude faster than querying and updating the real DOM. React diffs the objects, then surgically updates only what changed.
Reconciliation: React's Diffing Algorithm
When state or props change, React creates a new Virtual DOM tree and diffs it against the previous one. The diffing uses two heuristics to keep it O(n) instead of O(n³):
Heuristic 1: Different component types produce different trees
// Before:
<div><Counter /></div>
// After: <span><Counter /></span>
Different type (div → span) = destroy the old tree entirely, build a new one from scratch — even if children are identical. React never tries to "convert" one element type to another.
Heuristic 2: Keys identify list items across renders
// Before:
<li key="a">Alice</li>
<li key="b">Bob</li>
// After (item inserted at top): <li key="c">Carol</li> // React: new key "c" → INSERT <li key="a">Alice</li> // React: key "a" unchanged → MOVE <li key="b">Bob</li> // React: key "b" unchanged → MOVE
With keys, React performs 1 insert + 2 moves. Without keys, it would update every item's text and add one at the end — O(n) DOM mutations instead of the minimum needed.
The Key Bug (Most Common Interview Question)
// ❌ Using index as key — wrong when list order can change
{todos.map((todo, index) => (
<TodoItem key={index} todo={todo} />
))}
// If you delete item at index 0: // - React sees: key=0 now has different data // - It UPDATES all items instead of removing the first one // - Input fields, focus, and animations break
// ✅ Use stable, unique IDs {todos.map(todo => ( <TodoItem key={todo.id} todo={todo} /> ))}
Index as key is only safe when the list is static and never reordered, filtered, or sorted.
Render Phase vs Commit Phase
Reconciliation has two distinct phases:
Render Phase (pure, interruptible)
- React calls your component functions
- Diffs the Virtual DOM trees
- Computes the list of changes (effect list)
- Can be interrupted, paused, and restarted (with concurrent features)
- No DOM mutations happen here
Commit Phase (synchronous, uninterruptible)
- React applies the computed changes to the real DOM
- Runs refs and layout effects (
useLayoutEffect)
- Browser paints the screen
- Runs passive effects (
useEffect)
This separation is why getDerivedStateFromError (render phase) must be pure while componentDidCatch (commit phase) can have side effects.
Fiber: The Modern Reconciler
React Fiber (introduced in React 16) reimplemented the reconciler as a singly-linked list of work units called fibers. Each fiber represents one component.
Old reconciler: recursive, couldn't be paused. If a large component tree took 50ms to reconcile, the main thread was blocked for 50ms — causing dropped frames.
Fiber reconciler: breaks work into small units. React can process one fiber, check if the browser needs to handle input, and pause reconciliation to stay responsive. This enables:
- useTransition — mark updates as interruptible
- useDeferredValue — delay expensive re-renders
- Suspense — pause rendering while data loads
Why shouldComponentUpdate and React.memo Exist
React re-renders a component whenever its parent re-renders, even if the component's props haven't changed. React.memo and shouldComponentUpdate short-circuit reconciliation: if props are shallowly equal, skip the render and use the previous Virtual DOM tree.
// Without memo: renders every time parent renders
function StaticWidget({ label }) {
return <div>{label}</div>
}
// With memo: only renders when label changes const StaticWidget = React.memo(function({ label }) { return <div>{label}</div> })
Practice Virtual DOM and reconciliation questions at [JSPrep Pro](/auth).