How React Virtual DOM Works Under the Hood

When building modern user interfaces, developers often encounter a fundamental limitation of the browser: the Real DOM is not designed for frequent, high-performance updates. Every small change to the UI can trigger expensive operations such as layout recalculation, repainting, and reflow. As applications grow larger and more interactive, directly manipulating the DOM becomes increasingly inefficient and difficult to manage.
Libraries like React solve this problem by introducing a powerful abstraction called the Virtual DOM. This concept fundamentally changes how UI updates are calculated and applied, allowing developers to build highly interactive applications without sacrificing performance.
The Problem: Why Direct DOM Manipulation is Expensive
The Real DOM is a tree-like structure managed by the browser. Whenever a change is made, such as updating text or modifying an element, the browser must re-evaluate the layout and repaint parts of the screen. These operations are computationally expensive because the browser must ensure visual correctness, handle CSS calculations, and maintain rendering consistency.
In traditional JavaScript applications, developers often manually update the DOM using APIs like document.getElementById or innerHTML. While this works for small applications, it becomes inefficient when updates are frequent or when the UI is complex. Even a small change can cause large portions of the DOM to be re-rendered unnecessarily.
Introducing the Virtual DOM
To address this inefficiency, React uses a Virtual DOM, which is a lightweight JavaScript representation of the Real DOM. Instead of directly updating the browser’s DOM, React first updates this in-memory structure.
The Virtual DOM allows React to perform calculations and comparisons quickly because it operates purely in JavaScript, without triggering browser rendering processes. Once React determines what has changed, it applies only the minimal required updates to the Real DOM.
Real DOM vs Virtual DOM
Understanding the difference between these two is critical for grasping how React optimizes performance.
| Aspect | Real DOM | Virtual DOM |
|---|---|---|
| Nature | Actual browser structure | In-memory JavaScript object |
| Performance | Slow for frequent updates | Fast to manipulate |
| Update Strategy | Direct updates | Batched and optimized updates |
| Control | Managed by browser | Managed by React |
The Real DOM is expensive because every update affects the rendering pipeline, whereas the Virtual DOM allows React to control and optimize updates before touching the browser.
Initial Render Process in React
When a React application loads, the rendering process begins with components.
React components return JSX, which is then converted into JavaScript objects representing the Virtual DOM. This Virtual DOM tree is then used to generate the Real DOM elements that are rendered in the browser.
The process follows a clear flow:
Component → JSX → Virtual DOM → Real DOM
At this stage, the UI is fully rendered, and the application becomes interactive.
What Happens When State or Props Change
Once the application is running, any change in state or props triggers a re-render. However, React does not immediately update the Real DOM.
Instead, it creates a new Virtual DOM tree that reflects the updated state of the UI.
This is a crucial design decision because it allows React to compare the new UI with the previous one before making any actual changes in the browser.
Creation of a New Virtual DOM Tree
Whenever a component updates, React executes the component function again and generates a new Virtual DOM tree.
This new tree represents how the UI should look after the update.
At this point, React has two versions:
The previous Virtual DOM (before update)
The new Virtual DOM (after update)
The next step is to compare these two trees efficiently.
Diffing (Reconciliation) Explained
The process of comparing the old Virtual DOM with the new one is called reconciliation.
React uses an optimized diffing algorithm to identify differences between the two trees. Instead of comparing every node in detail, React uses heuristics such as:
Elements of different types produce different trees
Keys help identify stable elements in lists
Updates are usually localized rather than global
These assumptions allow React to perform comparisons in linear time rather than expensive deep comparisons.
How React Finds Minimal Required Changes
During the diffing process, React identifies exactly what has changed between the two trees.
For example:
If text content changes, only that text node is updated
If an element is added, only that element is inserted
If attributes change, only those attributes are modified
React does not re-render the entire UI. Instead, it builds a list of minimal changes required to update the Real DOM.
This process is often referred to as generating a “patch” or update plan.
Updating Only Changed Nodes in the Real DOM
After identifying the differences, React enters the commit phase, where it applies the updates to the Real DOM.
Instead of re-rendering the entire page, React updates only the nodes that have changed.
This selective update mechanism significantly reduces the number of DOM operations, which are the most expensive part of UI rendering.
Why This Approach Improves Performance
The Virtual DOM improves performance through several key optimizations:
It minimizes direct DOM manipulation
It batches multiple updates together
It ensures only necessary changes are applied
It reduces unnecessary reflows and repaints
By avoiding full re-renders and focusing only on what has changed, React ensures that applications remain fast and responsive even as they grow in complexity.
High-Level React Lifecycle: Render → Diff → Commit
The entire update process in React can be summarized in a structured lifecycle.
Render Phase
React creates a new Virtual DOM tree based on current state and props
Diff Phase (Reconciliation)
React compares the new tree with the previous one and identifies changes
Commit Phase
React updates the Real DOM with only the necessary modifications
This cycle repeats every time there is a state or prop change, ensuring the UI always reflects the latest data efficiently.
A Practical Mental Model
To understand this intuitively, think of React as a system that always asks the question: “What should the UI look like now?”
Instead of manually updating the DOM, you describe the UI for a given state, and React figures out how to update the screen efficiently.
For example, when updating a counter:
The state changes
A new Virtual DOM is created
React compares it with the old one
Only the changed text node is updated in the Real DOM
Everything else remains untouched.