Skip to main content

Real DOM vs Virtual DOM

The Real DOM is the browser's live, in-memory representation of an HTML document. The Virtual DOM is a lightweight JavaScript description of what a user interface should look like, used by libraries such as React to calculate updates before changing the browser DOM.

State changes
↓
React calculates the next UI
↓
React compares it with the previous UI
↓
Only necessary changes are committed to the Real DOM

One-line idea: The Real DOM is the actual page tree, while the Virtual DOM helps a UI library decide which parts of that tree need to change.

Core Concepts​

What Is the Real DOM?​

The Document Object Model (DOM) represents an HTML document as a tree of nodes. JavaScript can use browser APIs to read or modify its elements, attributes, text and event handlers.

<main>
<h1 id="title">Hello</h1>
</main>

The browser represents this markup approximately as:

Document
└── main
└── h1
└── "Hello"

A direct DOM update changes the live page:

document.getElementById("title").textContent = "Hello, Aman";

The DOM itself is not part of JavaScript. It is a browser-provided programming interface that JavaScript can use.

Why DOM Updates Can Become Expensive​

Changing a DOM node is not automatically slow. The cost often comes from browser work that may follow the change:

DOM change
↓
Style calculation
↓
Layout, when geometry changes
↓
Paint, when pixels change
↓
Compositing

A small, targeted update can be inexpensive. Performance problems are more likely when code performs many unnecessary reads and writes, repeatedly rebuilds large sections, or triggers layout work in a tight loop.

What Is the Virtual DOM?​

Virtual DOM is an informal name for an in-memory representation of the desired UI. It is made from JavaScript values rather than live browser nodes.

A simplified representation might look like this:

{
type: "h1",
props: {
children: "Hello, Aman"
}
}

The Virtual DOM is not a browser feature and it is not displayed directly. A rendering library uses it internally to work out how the Real DOM should change.

Render, Reconcile and Commit​

In React, a UI update can be understood in three main stages:

  1. Trigger: An initial render or state update requests work.
  2. Render: React calls components to calculate the next UI.
  3. Commit: React applies the necessary changes to the browser DOM.

Reconciliation is the process React uses while determining how a new rendering result relates to the previous one.

Previous rendering: <h1>Hello</h1>
Next rendering: <h1>Hello, Aman</h1>

Same element type + changed text
↓
Update the existing h1 text

Rendering a component does not always mean that React will change a DOM node. If the relevant output is unchanged, the commit phase may have no DOM work for it.

Element Types and Keys​

Element type helps React decide whether existing UI can be preserved.

// Previous output
<div>Profile</div>

// Next output
<span>Profile</span>

Because div and span are different element types, React replaces that part of the tree rather than updating the old element as though it were the same type.

For lists, a stable key identifies an item among its siblings:

users.map((user) => <UserRow key={user.id} user={user} />)

Keys help React match items when they are inserted, removed or reordered. They should come from stable data and should not be generated during rendering.

Real DOM vs Virtual DOM​

AreaReal DOMVirtual DOM
MeaningBrowser's live document treeJavaScript representation of desired UI
OwnerBrowserUI library or framework
Visible to usersProduces the actual pageNot displayed directly
Update styleCan be changed through DOM APIsCompared before changes are committed
Main strengthDirect control with no abstraction requiredDeclarative, predictable UI updates at scale
CostBrowser rendering work may follow changesUses extra JavaScript, memory and comparison work

Practical DOM Update Patterns​

Imperative Real DOM Update​

With direct DOM manipulation, the code describes how to perform each change.

counter.html
<button id="increment">Increment</button>
<p id="count">Count: 0</p>

<script>
let count = 0;

document.getElementById("increment").addEventListener("click", () => {
count += 1;
document.getElementById("count").textContent = `Count: ${count}`;
});
</script>

How it works:

  • The application stores the count in a variable.
  • The event handler finds the required DOM node.
  • The handler directly replaces that node's text.

This approach is suitable for small widgets and focused browser scripts. As interactions grow, manually keeping many DOM nodes synchronized with application state can become difficult.

Declarative React Update​

With React, the component describes what the UI should look like for the current state.

Counter.jsx
import { useState } from "react";

export default function Counter() {
const [count, setCount] = useState(0);

return (
<>
<button onClick={() => setCount((value) => value + 1)}>
Increment
</button>
<p>Count: {count}</p>
</>
);
}

How it works:

  • setCount triggers a render.
  • React calculates the component's next output.
  • During commit, React updates the text that changed in the Real DOM.

The advantage is not that JavaScript objects are magically faster than every direct DOM operation. The advantage is that React manages synchronization between state and a larger UI through a predictable declarative model.

Updating Many Nodes Efficiently Without a Virtual DOM​

Efficient updates do not require a Virtual DOM. Direct DOM code can batch work before attaching it to the live document.

render-list.js
const fragment = document.createDocumentFragment();

for (const product of products) {
const item = document.createElement("li");
item.textContent = product.name;
fragment.appendChild(item);
}

productList.replaceChildren(fragment);

How it works: The nodes are prepared away from the live document and committed together. This avoids repeatedly appending items one at a time to the visible list.

Choosing the Right Approach​

Use direct DOM APIs when:

  • the interaction is small and isolated
  • no UI framework is needed
  • precise browser-level control is important
  • the application already uses an imperative architecture

Use a declarative library such as React when:

  • many UI regions depend on changing state
  • reusable components are important
  • the interface has complex interactions
  • predictable updates and maintainability matter more than minimal abstraction

Benefits, Trade-Offs and Common Mistakes​

Virtual DOM benefits

  • Supports declarative, state-driven UI development.
  • Centralizes the work of reconciling UI changes.
  • Helps large component trees remain maintainable.
  • Minimizes unnecessary DOM operations during React's commit phase.

Trade-offs

  • Rendering and comparison require JavaScript execution.
  • The representation consumes additional memory.
  • A Virtual DOM is not automatically faster for small or static pages.
  • Poor state design and unnecessary component renders can still hurt performance.
MistakeBetter approach
Saying the Real DOM is always slowExplain that excessive updates and subsequent rendering work create the cost
Treating Virtual DOM as a browser APIDescribe it as a technique used by UI libraries
Assuming every React render changes the DOMSeparate React's render phase from its commit phase
Using array indexes or random values as changing keysUse stable IDs from the underlying data
Optimizing every component with memoizationMeasure first and optimize demonstrated bottlenecks
Mixing React state updates with uncontrolled DOM changesLet React own nodes that are rendered by React

Official References​

Summary​

The Real DOM is the browser's live page structure, while the Virtual DOM helps a library calculate and commit only the UI changes that are needed.

Application state changes
↓
Components describe the next UI
↓
React reconciles old and new results
↓
Required changes are committed to the Real DOM
↓
The browser performs layout, paint and compositing as needed