How React Virtual DOM Works Under the Hood
React is known for being fast and efficient when updating user interfaces. One of the main reasons behind this is the Virtual DOM and React's reconciliation process.
But what actually happens when a component updates?
How does React know what to change without rebuilding the entire page?
In this article, we'll walk through React's update lifecycle step-by-step and understand how the Virtual DOM helps React perform efficient UI updates.
The Problem: Direct DOM Manipulation Is Expensive
Before understanding the Virtual DOM, we first need to understand the problem it solves.
The browser's Real DOM (Document Object Model) represents the entire webpage as a tree structure.
Example:
<body>
<div>
<h1>Hello</h1>
<button>Click Me</button>
</div>
</body>
The browser converts this into a DOM tree internally.
Whenever JavaScript directly updates the DOM:
document.getElementById("title").innerText = "Updated";
the browser may need to:
Recalculate styles
Recalculate layout
Repaint the screen
Re-render portions of the page
These operations become expensive when updates happen frequently.
Imagine an application with hundreds or thousands of UI elements updating repeatedly. Direct DOM manipulation can quickly become a performance bottleneck.
Real DOM vs Virtual DOM
Real DOM
The Real DOM is:
The actual browser representation of the page
Managed by the browser
Expensive to update frequently
Causes layout and repaint operations
Virtual DOM
The Virtual DOM is:
A lightweight JavaScript representation of the UI
Stored in memory
Cheap to create and compare
Managed by React
Instead of updating the Real DOM immediately, React first updates the Virtual DOM and then determines the smallest set of changes needed.
Example Virtual DOM representation:
{
type: "div",
props: {
children: [
{
type: "h1",
props: {
children: "Hello"
}
}
]
}
}
Think of it as a blueprint of what the UI should look like.
Initial Render Process in React
Let's start with the first render.
Component:
function App() {
return <h1>Hello React</h1>;
}
When React renders this component:
Step 1: Component Executes
React calls the component function.
App()
Step 2: Virtual DOM Tree Is Created
React creates an in-memory tree representing:
<h1>Hello React</h1>
Step 3: Real DOM Nodes Are Created
React converts the Virtual DOM representation into actual DOM elements.
Step 4: DOM Is Mounted
React inserts those DOM nodes into the browser page. React uses DOM APIs such as appendChild() during the initial render.
At this point:
Component
↓
Virtual DOM
↓
Real DOM
↓
Screen
What Happens When State Changes?
Consider:
function Counter() {
const [count, setCount] = useState(0);
return (
<>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</>
);
}
When the button is clicked:
setCount(count + 1);
React does not directly update the DOM.
Instead, it starts a new render cycle.
Step 1: React Triggers a Re-render
A state update tells React:
"The UI may have changed. Recalculate it."
React executes the component again:
Counter()
using the updated state value.
Step 2: A New Virtual DOM Tree Is Created
Old Virtual DOM:
<h1>0</h1>
New Virtual DOM:
<h1>1</h1>
React now has:
Old Virtual DOM Tree
vs
New Virtual DOM Tree
What Is Diffing?
React now compares both trees.
This comparison process is called diffing.
Its goal is simple:
Find the smallest set of changes required to update the UI.
Instead of rebuilding the entire page, React compares:
Old:
<h1>0</h1>
New:
<h1>1</h1>
React notices:
Same element type (
h1)Only text content changed
Therefore:
Update text only
No need to replace the whole element.
This comparison process is part of React's reconciliation algorithm.
What Is Reconciliation?
Reconciliation is the overall process React uses to update the UI efficiently.
It includes:
Creating a new Virtual DOM tree
Comparing it with the previous tree
Determining differences
Applying minimal changes to the Real DOM
In simple words:
Reconciliation = Comparing old UI and new UI to figure out what actually changed.
How React Finds Minimal Changes
React follows several rules while diffing.
Rule 1: Different Element Types
Old:
<h1>Hello</h1>
New:
<p>Hello</p>
Since element types differ:
h1 → p
React replaces the entire node.
Rule 2: Same Element Type
Old:
<button className="blue">
New:
<button className="red">
React keeps the same button element and only updates the changed attribute.
Rule 3: List Items Use Keys
Example:
items.map(item => (
<li key={item.id}>{item.name}</li>
))
Keys help React identify which items:
Were added
Were removed
Were moved
Without keys, React has a harder time matching elements efficiently.
Updating Only Changed Nodes
After diffing, React produces a list of updates.
Example:
Change:
<h1>0</h1>
To:
<h1>1</h1>
Required update:
Update text node only
React then applies only that change to the Real DOM. It avoids unnecessary DOM operations and updates only the nodes that differ between renders.
Why This Improves Performance
The browser's DOM operations are expensive.
Creating a Virtual DOM tree is relatively cheap because it's just JavaScript objects in memory.
React's strategy is:
Many cheap JS operations
+
Few expensive DOM operations
Instead of:
Many expensive DOM operations
Benefits:
Faster UI updates
Reduced layout recalculations
Reduced repainting
Better performance for complex interfaces
React Render → Diff → Commit Flow
At a high level, every React update follows this lifecycle:
1. Render Phase
React executes components and creates a new Virtual DOM tree.
Component Function
↓
Virtual DOM Tree
2. Diff (Reconciliation) Phase
React compares:
Old Tree
vs
New Tree
and calculates the minimal set of updates.
3. Commit Phase
React applies those updates to the Real DOM. During re-renders, React performs only the minimal DOM operations calculated during rendering.
Calculated Changes
↓
Real DOM Update
↓
Browser Paint
Overall flow:
State / Props Change
↓
Component Re-renders
↓
New Virtual DOM Created
↓
Diffing (Reconciliation)
↓
Changes Calculated
↓
Commit Phase
↓
Real DOM Updated
↓
UI Updated
Final Thoughts
React's Virtual DOM is not a replacement for the browser DOM. Instead, it's a lightweight JavaScript representation that helps React determine what actually changed.
Whenever state or props update:
React creates a new Virtual DOM tree.
It compares it with the previous tree.
Diffing identifies differences.
Reconciliation determines minimal updates.
React updates only the affected DOM nodes.
This approach allows developers to write declarative UI code while React handles efficient DOM updates behind the scenes.
The mental model to remember is:
Render
↓
Create New Virtual DOM
↓
Compare With Previous Tree
↓
Find Differences
↓
Apply Minimal Changes
↓
Update Real DOM
And that's the core idea behind how React's Virtual DOM works under the hood.



