Introduction
Welcome back! In our last lesson, we established that React builds a component tree from lightweight JavaScript objects called React Elements. We learned that this tree defines your UI's structure and that data flows down it in a predictable, unidirectional way.
A key takeaway was that creating this tree of objects in memory is very fast and cheap compared to manipulating the actual browser DOM. Today, we'll explore how React leverages this fact to achieve its high performance. The learning outcome for this lesson is to explain how the Virtual DOM and React's reconciliation algorithm work to efficiently update the DOM.
We will cover:
- Why direct DOM manipulation can be slow.
- The concept of the Virtual DOM and the "diffing" process.
- The core rules of React's reconciliation algorithm.
- A look "under the hood" at React Fiber, the modern engine that powers this process.
This lesson is central to understanding the "magic" of React's performance and is a key part of the "React anatomy" you're interested in exploring.
The Problem: Expensive DOM Updates
Before diving into React's solution, it's crucial to understand the problem it solves. While modern JavaScript engines are incredibly fast, interacting with the browser's Document Object Model (DOM) is a different story.
Every time you change the DOM, the browser may have to do a lot of work:
- Recalculate styles: It needs to figure out which CSS rules apply to the changed element and its children.
- Recalculate layout (Reflow): It needs to determine the geometry of the page—the position and size of all elements.
- Repaint: It needs to redraw the pixels on the screen for the parts of the page that have changed.
Doing this for many elements frequently can lead to a sluggish user interface.
To see this in action, let's look at a Vanilla JS example where the entire UI is re-rendered on every update.
What is the virtual DOM in React? - LogRocket Blog
The article 'What is the virtual DOM in React?' from LogRocket provides a practical demonstration of this issue. It uses a simple timer to show how naively updating the DOM can cause problems, like losing focus in an input field.
Read the section titled 'How re-rendering impacts performance'. Pay close attention to the JavaScript code snippet and the GIF demonstrating the problem. You don't need to run the CodeSandbox example; the explanation in the article is sufficient.
As you saw, re-creating the HTML string and setting innerHTML on every tick is inefficient and breaks user interactions. This is the core problem React was designed to solve.
React's Solution: The Virtual DOM and Reconciliation
Instead of manipulating the real DOM directly on every change, React uses an abstraction layer: the Virtual DOM (VDOM).
As we touched on previously, the VDOM is simply the tree of React Element objects that lives in memory. It's a lightweight representation of the UI. Here’s the general process, known as reconciliation:
- Initial Render: React creates a VDOM tree and uses it to produce the initial real DOM.
- State Update: When a component's state or props change, React creates a new VDOM tree.
- Diffing: React compares the new VDOM tree with the previous one. This comparison process is called "diffing."
- Patching: React calculates the minimal set of operations needed to make the real DOM match the new VDOM. These changes are then "batched" and applied to the real DOM in one efficient go.

This approach ensures that only the necessary parts of the DOM are touched, avoiding expensive and unnecessary reflows and repaints.
What is the virtual DOM in React? - LogRocket Blog
The same LogRocket article provides a concise summary of these steps.
Read the section 'How the Virtual DOM works' to solidify your understanding of the Rendering, Diffing, and Patching steps.
The Diffing Algorithm
Comparing two trees can be a complex problem. A full comparison algorithm could have a complexity of O(n³), where n is the number of nodes. This would be too slow for a UI framework.
Instead, React uses a set of heuristics based on two key assumptions, which makes the algorithm much faster—O(n):
-
Different element types produce different trees. If a root element changes from a
<div />to a<span />, React doesn't try to compare them. It completely tears down the old tree and builds a new one from scratch. -
The developer can hint at which elements are stable across renders with a
keyprop. When rendering a list of items, if you don't provide keys, React might get confused if you add, remove, or reorder items, leading it to perform unnecessary work.
The key prop is your way of telling React, "This element corresponds to this specific piece of data." Keys only need to be unique among their siblings.
The following video provides an excellent and detailed explanation of how the diffing algorithm works, especially regarding lists and the importance of keys.
Understanding React's UI Rendering Process
Let's watch a segment from 'Understanding React's UI Rendering Process' that walks through the diffing algorithm.
Watch from 12:49 to 23:24. Focus on: What happens when element types differ (12:49 - 15:26). What happens when only props or attributes change (15:26 - 17:07). Most importantly, how React handles lists with and without keys, and why using an array index as a key can be problematic (17:07 - 23:24).
Understanding the role of keys is critical. While we will dedicate the next lesson to mastering list rendering, it's essential to see now that keys are not just for getting rid of console warnings—they are a fundamental tool for helping React's reconciliation algorithm perform efficiently.
Under the Hood: The Fiber Reconciler
The concepts of VDOM and diffing have been in React for a long time. However, the underlying implementation that executes this work was completely rewritten in React 16. This new engine is called React Fiber.
The old "Stack Reconciler" did all its work recursively and synchronously in a single pass. Once it started, it couldn't be interrupted. For complex UIs, this could block the main thread, making the app unresponsive to user input.
Fiber solves this by breaking the work down into smaller "units." It can do a bit of work, pause to let the browser handle higher-priority tasks (like an animation or user input), and then resume where it left off. This enables features like concurrent rendering.
To achieve this, Fiber splits the reconciliation process into two main phases:
-
Render Phase (Asynchronous & Interruptible):
- In this phase, React processes components, calls
rendermethods, and calculates the changes needed for the DOM. - It doesn't actually apply any changes. Since this work is done in memory and isn't visible to the user, React can safely pause, abort, or re-do it.
- Lifecycle methods like
getDerivedStateFromPropsand therenderfunction itself run in this phase. This is why you should never put side effects (like data fetching or manual DOM manipulation) in them.
- In this phase, React processes components, calls
-
Commit Phase (Synchronous & Uninterruptible):
- Once the render phase is complete, React has a finished "work-in-progress" tree.
- In this phase, React applies all the calculated changes to the real DOM. This process is synchronous to ensure UI consistency.
- Lifecycle methods like
componentDidMountandcomponentDidUpdaterun in this phase, after the DOM has been updated. This is the correct place for side effects that need to interact with the DOM.
To manage this, Fiber maintains two trees in memory:
- The
currenttree, representing what's currently on the screen. - The
workInProgresstree, a "draft" where React builds the updates.
Once the workInProgress tree is complete, the commit phase atomically swaps it to become the current tree.

Let's read a concise explanation of these phases.
Inside Fiber: in-depth overview of the new reconciliation algorithm in React
The article 'Inside Fiber: in-depth overview of the new reconciliation algorithm in React' is a fantastic deep dive. We won't read it all, but its definition of the two phases is perfect for our purposes.
Read the section titled 'General algorithm' and the two sub-sections that follow, 'Render phase' and 'Commit phase'. Focus on the distinction between the two phases (what they do, whether they are synchronous, and which lifecycle methods belong to each).
This two-phase model is a fundamental shift in how React works and is the key to its modern, more responsive architecture.
Conclusion
In this lesson, we went from the high-level "why" to the low-level "how" of React's rendering performance. You now have a much deeper model of what happens when you call setState.
Key Takeaways:
- Virtual DOM: An in-memory JavaScript representation of the UI. It's cheap to create and manipulate.
- Reconciliation: The overall process of rendering the UI, which includes creating a new VDOM, "diffing" it against the old one, and "patching" the real DOM with the minimal necessary changes.
- Diffing Heuristics: React's diffing algorithm is fast because it assumes different element types won't be compared and uses developer-provided
keysto efficiently track items in a list. - React Fiber: The modern reconciler that implements this process. Its key feature is the ability to perform work asynchronously.
- Render & Commit Phases: Fiber splits work into an interruptible render phase (calculating changes) and a synchronous commit phase (applying changes to the DOM), enabling a more responsive UI.
Preview of the Next Lesson:
We've established that keys are crucial for the reconciliation algorithm's performance. In the next lesson, we will compare list rendering techniques in vanilla JS with React's key-based reconciliation approach. We'll move from the theory of why keys matter to the practice of how to use them effectively to build dynamic lists.