What Is the Virtual DOM and Why Does React Use It?
Advertisement
🧩 Scenario
Architecture Walkthrough
What the Virtual DOM Is
A virtual DOM node is a plain JavaScript object that describes a UI element. When you write JSX, Babel compiles each element to React.createElement(type, props, ...children), which returns an object like { type: 'button', props: { className: 'primary', children: 'Save' } }. React builds a tree of these objects in memory representing the current desired UI state. This tree is the virtual DOM.
Because these are plain JavaScript objects, creating and comparing them is fast. The operations are string comparisons and property lookups in memory, not browser layout calculations.
Why Real DOM Operations Are Expensive
The browser DOM is not a simple data structure. Reading certain DOM properties (like offsetWidth, getBoundingClientRect, or scrollTop) forces the browser to recalculate layout before returning a value. Writing to DOM properties (inserting nodes, changing text, toggling classes) can invalidate the layout and trigger reflow and repaint. On pages with complex CSS, a single DOM mutation can cascade into hundreds of layout calculations.
Frequent small DOM mutations interleaved with reads are the worst case: the browser cannot batch its layout work because each read forces it to flush pending writes first.
How the Virtual DOM Helps
React separates the process into three phases. In the render phase, component functions run and produce a new virtual DOM tree. In the diff phase, the new tree is compared against the previous one to find what changed. In the commit phase, React applies only the changed portions to the real DOM in one pass.
This batching is the core benefit. If three state updates happen in the same event handler, React does not run three DOM commits. It batches all three, runs the render and diff once, and commits the net result. The real DOM is touched once, not three times. This is the mechanism behind React 18's automatic batching, which extends this to async contexts as well.
Key Code Explained
// A JSX element compiles to a plain JavaScript object
const button = <button className="primary" onClick={handleClick}>Save</button>;
// The compiled object (the virtual DOM node):
const button = {
type: 'button',
props: {
className: 'primary',
onClick: handleClick,
children: 'Save',
},
};
// React builds a tree of these objects, not real DOM nodes
// Comparing two such trees is pure JavaScript — fast and cheap
// When state changes, React runs the component again
function StockTicker({ symbol }: { symbol: string }) {
const [price, setPrice] = useState(0);
useEffect(() => {
const interval = setInterval(async () => {
const newPrice = await fetchPrice(symbol);
setPrice(newPrice); // triggers re-render
}, 1000);
return () => clearInterval(interval);
}, [symbol]);
return (
<div className="ticker">
<span className="symbol">{symbol}</span>
{/* Only this text node changes on each price update */}
<span className="price">{price.toFixed(2)}</span>
</div>
);
}
// After diff, React's commit only touches: textContent of the price span
// It does NOT recreate the div, re-attach className, or re-render the symbol span
// Contrast: naive imperative approach on a 50-row table
// ticker updates 10 prices at once:
prices.forEach(({ symbol, price }) => {
// Each querySelector + textContent assignment might force a layout recalc
document.querySelector(`#${symbol} .price`).textContent = price.toFixed(2);
});
// React's virtual DOM approach:
// 1. Render all 10 updated components → 10 new virtual DOM trees
// 2. Diff all 10 → 10 text node changes
// 3. Batch-commit all 10 text content mutations in one DOM pass
The performance advantage is not that creating JavaScript objects is faster than writing to the DOM directly. For a single operation, direct DOM manipulation is faster. The advantage is the batching: React accumulates all changes from a single state update cycle, diffs them against the previous tree, and commits the net result in one DOM pass. This prevents the interleaved read-write patterns that force repeated layout recalculations.
Tradeoffs
| Approach | Single update speed | Batch update efficiency | Developer experience |
|---|---|---|---|
| Direct DOM mutation | Fastest | Requires manual batching | Complex, error-prone |
| Virtual DOM (React) | Slightly slower | Automatic batching | Declarative, predictable |
| Compiled (Svelte) | Fast (no virtual DOM) | Granular reactivity | Low overhead, less ecosystem |
What Interviewers Actually Check
- Whether you know a virtual DOM node is a plain JavaScript object, not a browser concept
- Whether you can explain what makes real DOM mutations expensive
- Whether you can describe the render-diff-commit cycle
- Whether you know the virtual DOM is not always faster than direct DOM manipulation
- Whether you know the shadow DOM is unrelated to the virtual DOM
Follow-Up Questions
- React 19 introduces a compiler that can optimize re-renders without needing
useMemooruseCallback. How does this change the virtual DOM story? - How does React Native use the same virtual DOM concept for mobile without a browser DOM?
- What is incremental rendering (as implemented via React's fiber architecture) and why does it require the virtual DOM to be a separate phase from commit?
- Svelte compiles components to direct DOM mutation code at build time. What are the tradeoffs versus React's virtual DOM approach?
- How does React's
keyprop relate to the diffing algorithm that processes the virtual DOM?
Common Candidate Mistakes
- Saying the virtual DOM is a browser API or a browser feature when it is a library pattern
- Confusing the virtual DOM with the shadow DOM (an entirely different browser feature for web components)
- Claiming the virtual DOM is always faster than direct DOM manipulation for any single operation
- Not being able to describe what a virtual DOM node actually looks like (a plain JS object with type, props, children)
- Saying React "never touches the real DOM" when it absolutely does in the commit phase
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you explain what a virtual DOM node is as a plain JavaScript object?
- Can you explain why real DOM operations can be expensive in terms of reflow and repaint?
- Can you describe the render-diff-commit cycle in React?
- Can you explain the batching advantage: why one state update produces one commit even if multiple setters are called?
- Can you honestly describe when virtual DOM overhead is not worth it?
Summary
The virtual DOM is a JavaScript object tree that React maintains in memory as a description of what the UI should look like. When state or props change, React renders the affected components into a new virtual DOM tree, diffs it against the previous tree to find the minimum set of changes, and then commits those changes to the real DOM in a single batch pass.
The performance benefit is not that JavaScript object operations are inherently faster than DOM operations. A single direct DOM mutation is faster than the equivalent virtual DOM path. The advantage is batching: React accumulates all changes from a render cycle, applies the diff once, and commits the net result in one DOM pass. This prevents the worst-case scenario of interleaved DOM reads and writes that force the browser to recalculate layout repeatedly.
The virtual DOM also enables React's programming model: declarative components that describe the desired output, with React handling all imperative DOM mutations. This tradeoff, slightly more overhead per operation in exchange for automatic batching and a declarative model, is the core value proposition. For very performance-critical, simple UIs with known change patterns, direct DOM manipulation can be faster. For complex, dynamic applications, React's model is more maintainable and predictable.
Is the virtual DOM the same as the shadow DOM?
No. The shadow DOM is a browser API for encapsulating styles inside custom elements (used in web components). The virtual DOM is a React-specific JavaScript technique for batching and minimizing real DOM mutations. They are unrelated.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement