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:
- Trigger: An initial render or state update requests work.
- Render: React calls components to calculate the next UI.
- 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
| Area | Real DOM | Virtual DOM |
|---|---|---|
| Meaning | Browser's live document tree | JavaScript representation of desired UI |
| Owner | Browser | UI library or framework |
| Visible to users | Produces the actual page | Not displayed directly |
| Update style | Can be changed through DOM APIs | Compared before changes are committed |
| Main strength | Direct control with no abstraction required | Declarative, predictable UI updates at scale |
| Cost | Browser rendering work may follow changes | Uses 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.
<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.
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:
setCounttriggers 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.
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.
| Mistake | Better approach |
|---|---|
| Saying the Real DOM is always slow | Explain that excessive updates and subsequent rendering work create the cost |
| Treating Virtual DOM as a browser API | Describe it as a technique used by UI libraries |
| Assuming every React render changes the DOM | Separate React's render phase from its commit phase |
| Using array indexes or random values as changing keys | Use stable IDs from the underlying data |
| Optimizing every component with memoization | Measure first and optimize demonstrated bottlenecks |
| Mixing React state updates with uncontrolled DOM changes | Let 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