Hello! Welcome back.
In our last lesson, we demystified JSX, discovering that it's syntactic sugar for React.createElement() function calls. We saw how these calls produce a tree of simple JavaScript objects that describe the UI you want to build.
Now, we'll answer the crucial follow-up question: What does React do with this tree of objects? This brings us to the very heart of what makes React so efficient.
Today's lesson addresses the learning outcome: Describe the concept of the Virtual DOM and how React's reconciliation algorithm uses it to perform efficient DOM updates.
We will cover:
- The concept of the Virtual DOM (VDOM) as an in-memory UI representation.
- The performance problem with direct DOM manipulation.
- The process of "reconciliation" and the "diffing" algorithm React uses.
- The key heuristics that make this algorithm fast, connecting to your computer science background.
- The distinction between reconciliation (figuring out changes) and rendering (applying changes).
Understanding this process is fundamental to grasping React's performance model and writing optimized applications.
The Problem with the Real DOM
Before diving into React's solution, it's important to understand the problem it solves. The browser's Document Object Model (DOM) is a tree-like structure representing your webpage. Manipulating it directly using APIs like document.getElementById() or appendChild() is what causes the browser to re-calculate layouts, repaint pixels, and update the screen.
These operations are computationally expensive. If you have a complex UI with many elements and frequent updates (e.g., from user input or streaming data), making numerous direct changes to the DOM can lead to performance bottlenecks, dropped frames, and a sluggish user experience. You've likely encountered this when building projects like your Log Viewer, where rendering 100,000+ lines could freeze the UI if not handled carefully.
The Virtual DOM: A Blueprint for the UI
React's solution is to introduce a layer of abstraction: the Virtual DOM (VDOM).
The VDOM is an in-memory representation of the real DOM. It's a lightweight tree of plain JavaScript objects that mirrors the structure of the UI. The React element objects we discussed in the last lesson are the building blocks of this tree.
Here's the core workflow:
- Initial Render: When your application first loads, React creates a VDOM tree and uses a renderer (like
react-dom) to build the initial real DOM from it. - State Update: When a component's state changes (e.g., via a
useStatehook), React creates a new VDOM tree representing the updated UI. - Comparison: React now has two VDOM trees in memory: the previous one and the new one. It compares them to figure out the exact differences.
- Batch Update: React then calculates the most efficient, minimal set of operations needed to update the real DOM to match the new VDOM. These changes are batched together and applied in one go.
This process avoids unnecessary changes to the real DOM. Instead of telling the browser how to change the UI step-by-step, you declaratively tell React what the UI should look like, and React handles the efficient transition.
Reconciliation and the "Diffing" Algorithm
The process of comparing two VDOM trees to find the differences and update the DOM is called reconciliation. The algorithm React uses to do this is often called the "diffing" algorithm.
From a computer science perspective, the problem of finding the minimum number of edits (insertions, deletions, and modifications) to transform one tree into another is known as the "Tree Edit Distance Problem."
A Closer Look at React's Reconciliation Algorithm
To understand the complexity of this problem and why React's solution is so clever, let's read this section from an article by Raj Rajhans.
Please read the section titled 'The Tree Edit Distance Problem'. Note the time complexities mentioned for brute-force and dynamic programming solutions (O(nÂł)). This highlights why a naive approach is infeasible for real-time UI updates.
As the article explains, state-of-the-art algorithms have a complexity of O(nÂł), which is far too slow for user interfaces that need to respond in milliseconds. If you had a tree of 1000 elements, this could mean a billion comparisons.
React's Heuristic O(n) Algorithm
To solve this, React implements a heuristic algorithm with a much better time complexity of O(n). It achieves this remarkable speed by making two pragmatic assumptions about how developers build UIs.
How Does React Actually Work? React.js Deep Dive #1
This video from Philip Fabianek provides an excellent overview of the reconciliation process and the two key assumptions that make React's diffing algorithm so fast.
Watch from 05:34 to 08:19. Focus on: How React generates the VDOM tree (tree of elements). The core idea of the 'Diffing algorithm' to find the smallest number of operations. The two crucial assumptions React makes to achieve its O(n) performance.
Let's break down those two assumptions:
1. Two elements of different types will produce different trees.
When comparing two VDOM nodes, if their types are different (e.g., a <div> is replaced by a <span>, or a <Header> component is replaced by an <Article> component), React doesn't bother trying to compare them further. It assumes the entire subtree is different. It will:
- Destroy the old component instance and its corresponding DOM nodes.
- Call unmounting lifecycle methods.
- Build the new component and its subtree from scratch.
This is a reasonable heuristic because radically changing a component's type usually implies a completely different structure and logic.
2. The developer can hint at which child elements are stable across renders using a key prop.
When comparing a list of children (e.g., <li> elements in a <ul>), a naive diff would compare by index. If you add a new item to the beginning of the list, React would see that every single <li> has changed and would re-render all of them.
To solve this, React uses the key prop. A key is a special string attribute you provide to elements in a list. It should be stable and unique among its siblings. When React sees keys, it can efficiently match elements between the old and new VDOM trees, even if their order has changed.
The Diffing Algorithm in Practice
Based on these heuristics, the reconciliation process follows a clear set of rules.
How Does React Actually Work? React.js Deep Dive #1
Let's continue with the same video to see concrete examples of how the diffing algorithm handles different update scenarios.
Watch from 09:17 to 11:04. Pay attention to how React handles: Different types: Tearing down the old tree. Same types, different attributes: Updating only the attributes. List updates without keys: Inefficient updates when adding to the start. List updates with keys: Efficiently identifying and inserting the new element.
To summarize the algorithm's behavior:
- Comparing Root Elements:
- Different Types: Tear down the old tree and build the new one.
- Same Type:
- DOM Elements (
div,span...): Keep the underlying DOM node and only update the changed attributes (likeclassName,style). Then, recurse on the children. - Component Elements (
<MyComponent>...): The component instance remains the same, so state is preserved. React calls the component'srendermethod and diffs the new result against the previous result.
- DOM Elements (
- Recursing on Children:
- Without Keys: React iterates over both lists of children simultaneously and generates a mutation whenever there’s a difference. This is problematic for insertions or re-orderings.
- With Keys: React builds a hash map (or similar data structure) from the keys of the children in the original tree. It then iterates through the new children. If a new child's key exists in the map, it's a match (and potentially a move). If not, it's a new element (an insertion). Any keys left in the map at the end correspond to elements that were removed (a deletion).
We will dedicate our next lesson entirely to the key prop, as its correct usage is critical for both performance and correctness in React applications.
Reconciliation vs. Rendering
It's useful to distinguish between React's core logic and the platform-specific renderers.
- React (the core library): This package contains the logic for creating components and elements, and importantly, for reconciliation. It runs the diffing algorithm to determine what has changed. It is platform-agnostic.
- React DOM / React Native (the renderers): These packages are responsible for the rendering. They take the list of changes computed by React core and translate them into platform-specific operations (e.g., manipulating the browser DOM or creating native UI views on mobile).
This separation is what allows React to be used on multiple platforms. The reconciliation logic is the same, but the final rendering step is tailored to the environment.
A Glimpse into the Modern Reconciler: React Fiber
Since React 16, the reconciliation algorithm has been completely rewritten. The new engine is called React Fiber.
While the core diffing logic and heuristics we've discussed remain the same, Fiber changes how the work is done. The old reconciler (the "Stack Reconciler") was synchronous. Once it started a render, it couldn't be interrupted, which could lead to a frozen UI during large updates.
React Fiber addresses this by breaking reconciliation work into small, pausable "units of work" called fibers.
This article from the LogRocket Blog gives a fantastic deep dive into React Fiber. We'll focus on what it is and the problems it solves.
Read the sections 'What is React Fiber?', 'React’s stack reconciler', and 'How does React Fiber work?'. Focus on: The limitations of the old synchronous 'stack' reconciler, especially regarding the 16ms frame budget and dropped frames. The goals of Fiber: to enable pausing, resuming, and prioritizing rendering work. The core idea that a 'fiber' is like a 'virtual stack frame', allowing React to manage the execution of work instead of being blocked by the JavaScript call stack.
In essence, Fiber is a re-implementation of the call stack specialized for React components. This allows React to:
- Split rendering work into chunks and spread it over multiple frames.
- Prioritize different types of updates (e.g., a user typing in an input is higher priority than rendering a newly fetched list of data).
- Pause, abort, or reuse work as new, higher-priority updates come in.
This asynchronous, non-blocking reconciliation is what enables modern React features like Transitions and Suspense, leading to much more responsive user interfaces.

Conclusion
In this lesson, we've unpacked the core concepts behind React's impressive performance. By abstracting away direct DOM manipulation, React can make smart, efficient decisions about how to update the UI.
Key Takeaways:
- The Virtual DOM is an in-memory JavaScript representation of the UI, serving as a blueprint for the real DOM.
- Direct DOM manipulation is slow; React minimizes it by batching updates.
- Reconciliation is the process of comparing two VDOM trees to determine the minimal set of changes needed.
- React uses a fast, heuristic O(n) diffing algorithm based on two assumptions: different element types produce different trees, and
keyprops identify stable elements. - React Fiber is the modern implementation of the reconciler, which makes rendering asynchronous and non-blocking, improving UI responsiveness.
What's Next?
We've established the theoretical foundation of the diffing algorithm and mentioned the importance of the key prop. In our next lesson, we will dive deep into this topic: Explain the critical role of the 'key' prop in lists for optimizing reconciliation and preserving component state. We'll explore practical examples, common pitfalls (like using array indices as keys), and the consequences for component state when keys are not handled correctly.