
By Sanket Shah
Feb 25, 2026
8 min read

By Sanket Shah
Feb 25, 2026
8 min read
How does React decide which DOM nodes truly need updates? React reconciliation compares virtual DOM trees, applies efficient diffing, and uses Fiber scheduling to update only necessary elements, preserving performance and responsiveness.
React reconciliation sits at the heart of every modern React application. It decides which changes to keep and which to discard when your UI updates.
But what actually happens when your component re-renders? And how does React know which DOM nodes to touch and which ones to leave alone?
Let's break down how the virtual DOM works, how the diffing algorithm compares two trees, and how React Fiber schedules rendering work without dropping frames.
You will also build a simplified reconciler to see how React updates only the parts that matter, rather than rewriting the entire page.
The Document Object Model is a tree structure. Every element on the page becomes a node in the DOM tree. When you change something, the browser recalculates the layout and painting.
Direct manipulation of the actual DOM is slow. Updating many DOM nodes during user interactions can cause visible delays. When you repeatedly render large parts of the entire ui repeatedly, performance drops fast.
If you manually update the real DOM, you must:
This approach works for small scripts. It becomes hard when the whole app grows. Updating the whole tree every time is not realistic.
React introduces the virtual DOM. It is an in-memory representation of the DOM tree. Instead of modifying the actual DOM directly, React first works with a virtual DOM representation.
When a component re-render happens, React creates a new virtual DOM. It does not immediately touch the real dom. It builds a new virtual DOM tree in memory.
Then it compares:
This comparison uses a diffing algorithm. After computing differences between two trees, React performs the minimal DOM operations needed to reflect the changes.
That is why the virtual DOM improves performance. It avoids unnecessary rework on the entire subtree.
When you write JSX, React creates objects known as React elements. These React elements define the UI's appearance. They are lightweight. They are not DOM nodes.
Each time your component re-renders, React creates a new set of React elements. These represent the next render tree.
So what changes when you update a component's state?

React creates this new virtual DOM quickly because it is just JavaScript objects. No layout recalculation. No painting. Just objects.
The reconciliation process is the core algorithm that compares two trees. React’s reconciliation algorithm assumes:
When comparing two elements of the same type, React updates properties on the existing DOM nodes. When the types differ, React destroys the old tree and builds a new tree.
This diffing algorithm runs during every re render.
At a high level:
This reconciliation algorithm operates tree recursively. It performs tree traversal from the root node down to each child fiber.
Before React 16, the stack reconciler processed updates in a single pass. It used a stack frame for each component instance. Once it started rendering, it could not pause. Long rendering work blocked the main thread.
React Fiber changed that.
React Fiber introduced incremental rendering. Instead of processing the whole tree in a single stack frame, it splits the work into multiple frames.
Each fiber represents a unit of work. A fiber represents a component instance and stores:
Together, fibers form a fiber tree. This fiber tree mirrors the virtual DOM tree.
React Fiber allows scheduling. Rendering can pause between frames and resume in the next animation frame. This avoids dropping frames during heavy updates.
When a high-priority function, such as user input, occurs, React can pause low-priority updates and process the next unit first.
That means:
This is the current implementation of the reconciliation algorithm inside modern React.
The diffing algorithm compares two trees: the old tree and the new virtual DOM tree.
If two elements have different component types:
This applies to both class components and function components.
When types match, React preserves the existing component instance and updates its props.
When comparing host components like div or span, React checks:
It then updates only the changed DOM elements in the actual DOM.
React updates do not recreate DOM nodes unless needed. Only the parts that differ get patched.
When rendering arrays, React relies on the key attribute or key prop.
Without a key prop, React compares items by index. That can cause unnecessary reoperations if order changes.
With stable keys:
Keys provide hints to the diff algorithm so it can accurately compare two elements across different trees.
The reconciliation process has two major phases:
In this phase:
This phase can pause. It uses multiple frames.
During the commit phase:
This phase is synchronous. Once started, it completes.
Now, let us simulate the core algorithm.
Create simple objects:
This becomes your virtual tree.
Write a diff algorithm:
If two elements share the same type, reuse the node. If not, replace the whole tree.
Perform tree traversal:
When lengths differ, add or remove DOM nodes.
This mimics how React compares the new virtual DOM to the old tree.
Collect patches:
Apply them to the dom tree.
Now you have a tiny engine that:
Every React developer hears the word re render and instantly thinks, “Is my whole UI about to refresh?” Not quite. A re-render is simply React recalculating the UI based on new data. It is a comparison process, not a full redraw.
A re render starts when:
React creates a new virtual DOM tree. It compares it with the old tree stored in the fiber tree.
If nothing changed, no dom operations occur.
If changes exist, React updates only the parts that differ.
This is why re-render does not mean redraw the entire page. It means compute differences between two trees and patch dom nodes.
Under the hood, React is constantly managing data structures, not pixels. What you see in the browser is just the final outcome. Internally, React works with trees, fibers, and a carefully designed diffing algorithm that determines which parts should change and which should stay untouched.
The same reconciler logic works for:
React creates abstraction layers so that the app's logic remains consistent across platforms.
If you change the root node type, React replaces the entire subtree.
Example:
<div>
<Header />
</div>
becomes:
<section>
<Header />
</section>
Different host components mean replacing dom nodes from that root node downward.
That is why matching same type matters.
Modern interfaces change constantly. State updates, prop changes, and parent re-render cycles can make it feel like the whole app is refreshing every time something small happens. That misunderstanding often leads developers to fear performance issues and over-optimize too early. The real challenge is not frequent updates, but understanding how two trees are compared, how the fiber tree tracks rendering work, and how React updates DOM nodes without rewriting the whole tree.
When you build a small reconciler yourself, log each comparison, switch component types, and remove keys, the process becomes clear. You see the problem, the structured solution, and the logic behind it. React reconciliation stops feeling mysterious and starts looking like a controlled tree comparison with scheduling. That shift changes how you design every React application going forward.
Table of contents
What is the difference between virtual dom and actual dom?
Does React update the whole DOM on every re render?
What role does react fiber play?
Why are keys important in lists?