Skip to main content

Command Palette

Search for a command to run...

How React Virtual DOM Works Under the Hood

Updated
7 min read

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:

  1. Recalculate styles

  2. Recalculate layout

  3. Repaint the screen

  4. 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:

  1. Creating a new Virtual DOM tree

  2. Comparing it with the previous tree

  3. Determining differences

  4. 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:

  1. React creates a new Virtual DOM tree.

  2. It compares it with the previous tree.

  3. Diffing identifies differences.

  4. Reconciliation determines minimal updates.

  5. 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.