React's reconciliation algorithm diffs virtual DOM trees in O(n) using two heuristics — different element types tear down the subtree, same types compare attributes — while the Fiber architecture makes this work interruptible so the browser stays responsive during large updates.
Lightweight JavaScript object tree describing the UI — React diffs old vs new virtual DOM and applies only the minimum necessary changes to the real DOM.
Two heuristics: different element types tear down the entire subtree, same types update attributes in place. Keys match list children by identity rather than position.
Linked-list of fiber nodes (one per component) replaces recursive stack traversal — enables pausing, resuming, and prioritizing rendering work.
Render phase (interruptible) computes the new tree with no DOM mutations. Commit phase (synchronous) applies all changes atomically.
Updates are assigned priority levels — user interactions interrupt in-progress lower-priority rendering, enabling useTransition and useDeferredValue.
Understanding React's internals — the virtual DOM, reconciliation, and Fiber architecture — explains why React performs well and why certain patterns (like stable keys and component structure) matter.
The virtual DOM is a lightweight JavaScript object representation of the actual DOM. When state changes, React creates a new virtual DOM tree describing the desired UI, diffs it against the previous tree, and applies only the necessary changes to the real DOM. This is faster than directly manipulating the DOM for most updates because JavaScript object comparison is cheap while DOM operations are expensive.
A React element is just a plain object: { type: 'div', props: { className: 'card', children: [...] } }. Components return these element descriptions — React handles turning them into actual DOM nodes.
Generic tree diffing algorithms have O(n³) complexity — unusable for UI with thousands of nodes. React achieves O(n) diffing with two heuristics:
Different element types produce different trees: If the root element changes type (e.g., <div> to <span>, or <Article> to <Comment>), React tears down the entire old subtree (unmounting all components, destroying DOM nodes) and builds the new tree from scratch. This seems aggressive but is correct in practice — different element types rarely produce similar DOM structures.
Same element type updates in place: If the type matches, React keeps the same DOM node and only updates the changed attributes. For components, the instance stays alive — React updates props and re-renders.
For children lists, React uses keys to match elements across renders. Without keys, React compares children by position. With keys, React matches by key value — enabling efficient insertions, deletions, and reorderings without destroying unrelated elements.
Introduced in React 16, Fiber replaced the old recursive "stack reconciler" with an incremental rendering engine. The key insight: rendering a large component tree synchronously blocks the browser's main thread, causing dropped frames and unresponsive input.
Fiber breaks rendering into units of work (one "fiber" per component instance). Each fiber is a JavaScript object containing:
This linked-list structure means React can pause work after any fiber, let the browser handle user input or paint a frame, and then resume exactly where it left off. The old stack reconciler couldn't pause — once it started diffing a tree, it had to finish before yielding to the browser.
Fiber splits rendering into two phases:
useLayoutEffect), and then runs passive effects (useEffect). This phase cannot be interrupted — the DOM must stay consistent.React assigns priority lanes to updates. User interactions (clicks, typing) get high priority. Data fetches or transitions get lower priority. High-priority updates can interrupt in-progress low-priority rendering. This is the mechanism behind useTransition and useDeferredValue — they mark updates as low-priority so they don't block user interactions.
Fiber maintains two trees: the "current" tree (what's on screen) and the "work-in-progress" tree (being built). Once the work-in-progress tree is complete, React swaps them in one atomic commit — similar to double buffering in graphics programming. This ensures the UI is never in a half-updated state.
The virtual DOM is a JavaScript representation of the UI that enables efficient diffing. Reconciliation compares virtual DOM trees in O(n) using two heuristics: different types unmount, same types update in place. Fiber makes this process interruptible by using a linked-list structure instead of recursive calls, splitting work into render (interruptible) and commit (synchronous) phases. Priority lanes enable concurrent features like useTransition.
Fun Fact
The Fiber rewrite (React 16) took over two years and was one of the largest internal rewrites in open-source history. The React team rewrote the entire reconciler while maintaining backward compatibility — most apps upgraded from React 15 to 16 without changing a single line of code, despite the engine underneath being completely different.