Virtual DOM and Reconciliation in React Explained
Virtual DOM and Reconciliation
~16 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
So far we've taken React for granted: call setState, and "somehow" the page updates. From here on, we look UNDER the hood – what EXACTLY happens between a setState call and the visible change in the browser?
The problem React set out to solve
Direct DOM manipulation (document.querySelector(...).innerHTML = ...) is EXPENSIVE: every change can trigger layout recalculation ("reflow") and repainting of the entire affected area. With frequent, fine-grained UI updates (like our RenderCounter, updating on every click), naively "regenerating and inserting the entire HTML on every state change" would be catastrophically slow.
What the Virtual DOM actually is
The Virtual DOM is NOT some secret browser API – it's a plain JavaScript object tree that React itself maintains, a lightweight DESCRIPTION of what the real DOM SHOULD look like. When you write JSX like <h1>{{name}}</h1>, Babel compiles it to React.createElement('h1', null, name) – the result is a plain object like {{ type: 'h1', props: {{ children: name }} }}, NOT a real DOM node.
// The JSX you write:
<h1 className="title">Hello World</h1>
// ...gets compiled into this JavaScript call:
React.createElement('h1', { className: 'title' }, 'Hello World')
// ...which returns THIS plain object (heavily simplified):
{
type: 'h1',
props: { className: 'title', children: 'Hello World' },
}Reconciliation: the comparison-and-update process
Reconciliation is the process React uses to figure out what changed between two Virtual DOM trees. On every setState, React creates a COMPLETELY NEW Virtual DOM tree for the affected component (and its children) and compares it against the previous one ("diffing"). Only the parts that ACTUALLY changed get passed to the real DOM as minimal instructions – a targeted textContent change instead of a full innerHTML rebuild.
Why React doesn't try every possible tree comparison
A mathematically COMPLETE tree comparison between two arbitrary trees is computationally very expensive (O(n³) in the general case). React instead uses two practical heuristics that reduce the comparison to O(n):
- Different element types produce different trees. If a
<div>becomes a<span>at the same spot, React throws away the ENTIRE old subtree and builds fresh, instead of hunting for commonalities. - Keys identify elements across renders. Exactly the
keyprop from the.map()call in ourProductListPage(chapter 7 of "React for Beginners") – without stable keys, React compares list elements only by POSITION, which causes unnecessary re-creation on insertions/deletions in the middle of a list.
Making the key problem visible in our own project
Remember ProductCard's isFavorite state from "React for Beginners"? It lives INSIDE the component instance, tied to its key. Open the React DevTools "Components" tab, select a ProductCard, manually set isFavorite to true (editable right in the DevTools panel). Then switch to the next pagination page and back – with stable key values (product.sku), React does NOT create a new component instance for RECURRING products; their local state would be preserved (for the same product). Without stable, unique keys (e.g. using the array INDEX as key for a changing list), React would incorrectly associate state with POSITIONS instead of REAL entities here.
Achtung: This is EXACTLY why "React for Beginners" chapter 7 warned against key={{index}}: the index is not a stable identity of the thing itself, only of its current position. If an element gets inserted before position 0, the ENTIRE subsequent state "shifts" by one position from React's perspective – sometimes leading to bizarre bugs (wrong input focus, mismatched local state).
Connecting this to the last few chapters
Now everything from the last few chapters ties together: React.memo (chapter 31) skips reconciliation for a component ENTIRELY when props are unchanged – the expensive diff calculation gets avoided altogether. Virtualization (chapter 32) reduces how many elements even exist in the trees being compared. Stable keys ensure reconciliation does MINIMAL, not MAXIMAL, work on list changes.
Tipp: Rule of thumb: the "Virtual DOM" isn't performance magic in itself – raw direct DOM manipulation can even be FASTER than React's diffing overhead in micro-benchmarks. The real value lies elsewhere: you write declarative code ("here's what the UI should look like for THIS state"), React handles figuring out the MOST EFFICIENT translation into real DOM operations – a huge productivity win that makes most apps fast ENOUGH without you ever having to write manual DOM diffs.