Mon Apr 08 - Written by: Janmejay Chatterjee
Peeling Back the Layers, An Inside Look at How React Works
Dive into the inner workings of the React.js framework and uncover the mechanics behind its efficient virtual DOM and lightning-fast updates.
React js is one of the most popular front-end libraries in the world. Developed by Facebook, it has revolutionized the way we build web applications. For the last 3 years, I have build tens of user interface and I have always been fascinated by how React works under the hood. In this article, I will take you on a journey through the inner workings of React. We will explore the virtual DOM, the reconciliation process, and the diffing algorithm that makes React so fast and efficient.
Did you just said Virtual DOM?
Anyone in web development should be familiar with the Document Object Model (DOM). The DOM is a tree-like structure that represents the structure of an HTML document. It is a live, in-memory representation of the HTML elements on a web page. The DOM is used by browsers to render web pages and allows developers to manipulate the content and structure of a web page using JavaScript.

The issue with the DOM is that it can be extremely slow to update. When you make changes to the DOM, the browser has to re-render the entire page, which can be a costly operation. This is where React takes a different approach. Instead of directly manipulating the DOM, React uses a virtual DOM to represent the state of the UI. The virtual DOM is a lightweight copy of the real DOM that React uses to keep track of the changes that need to be made to the UI.
The virtual is not the opposite of the real, but rather the opposite of the fixed; it is a tendency, a potentiality, a possibility. The virtual is a mode of being, not a lack of being.
This is also the reason why React is so fast. When you make changes to the UI, React updates the virtual DOM instead of the real DOM. It then compares the virtual DOM with the real DOM and only makes the necessary changes to the real DOM. Not everything triggers a re-render though. This is also the reason why for beginners coming to this framework, it can be a bit confusing to understand why some changes are reflected and some are not. Also we don’t write React code in plain JavaScript. We use JSX which is a syntax extension for JavaScript that looks similar to HTML. JSX makes it easier to write and read React code.
import React, { useState } from "react";
function App() {
let name = "John";
const handleClick = () => {
name = "Mary";
};
return (
<div>
<h1>{name}</h1>
<button onClick={handleClick}>Change Name</button>
</div>
);
}
// This will not re-render the component, even though the name variable has changed
Everyone has been there. You change the name variable in the handleClick function but the UI does not update. This is because React does not re-render the component when the state of the component changes. You have to write your code a certain way to make sure that React knows when to re-render the component. This is where state and props come in.
State
State is a built-in React object that stores the state of a component. When the state of a component changes, React re-renders the component. You can use the useState hook to create state variables in a functional component. The useState hook returns an array with two elements: the current state value and a function that allows you to update the state value.
import React, { useState } from "react";
function App() {
const [name, setName] = useState("John");
const handleClick = () => {
setName("Mary");
};
return (
<div>
<h1>{name}</h1>
<button onClick={handleClick}>Change Name</button>
</div>
);
}
// This will re-render the component when the name state changes
Props
Props are short for properties and are used to pass data from one component to another. Props are read-only and cannot be changed by the component that receives them. You can pass props to a component by adding attributes to the component tag.
import React from "react";
function App() {
return <Greeting name="John" />;
}
function Greeting(props) {
return <h1>Hello, {props.name}</h1>;
}
Reconciliation, what’s that?
Reconciliation is the process by which React updates the real DOM based on changes to the virtual DOM. When you make changes to the UI, React updates the virtual DOM and then compares it with the real DOM. React then calculates the minimum number of changes needed to update the real DOM to match the virtual DOM. This process is known as reconciliation. (Finally all those Leetcode trees problem are coming in handy)

The reconciliation process is what makes React so fast and efficient. By only updating the parts of the real DOM that have changed, React minimizes the amount of work that the browser has to do to re-render the page. This is why React is able to update the UI so quickly and efficiently. This process of comparing the virtual DOM with the real DOM and updating the real DOM is known as the diffing algorithm.
Diffing Algorithm
When diffing two trees, React takes a top-down approach. It starts at the root of the tree and compares the two trees node by node. React uses three rules to determine how to update the real DOM based on changes to the virtual DOM:
- Components of Different Types: If the type of a component has changed, React will unmount the old component and mount the new component in its place.
- DOM Elements of Different Types: If the type of a DOM element has changed, React will create a new element and replace the old element in the real DOM.
- Keyed Elements: If two elements have the same type, React will compare the keys of the elements. If the keys are the same, React will update the element. If the keys are different, React will unmount the old element and mount the new element in its place.
By default, when recursing on the children of a DOM node, React just iterates over both lists of children at the same time and generates a mutation whenever there’s a difference.
For example, when adding an element at the end of the children, converting between these two trees works well.
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
React will match the two first children and see that the third child does not exist in the original tree. It will then create a mutation for the third child. If you implement it naively, the algorithm will have a time complexity of O(n^3) where n is the number of elements in the tree. This is because for each element in the tree, you have to compare it with every other element in the tree. This is not efficient and can lead to performance issues when rendering large trees.
Keys
In order to optimize the diffing algorithm, React uses keys. Keys are unique identifiers that you can assign to elements in a list. When React compares two lists of elements, it uses the keys to determine which elements have changed, been added, or been removed. By using keys, React can update the real DOM more efficiently and minimize the number of changes that need to be made.
<ul>
<li key="1">first</li>
<li key="2">second</li>
</ul>
<ul>
<li key="1">first</li>
<li key="2">second</li>
<li key="3">third</li>
</ul>
Tradeoffs
It is important to remember that the reconciliation algorithm is an implementation detail. React could rerender the whole app on every action; the end result would be the same. Just to be clear, rerender in this context means calling render for all components, it doesn’t mean React will unmount and remount them. It will only apply the differences following the rules stated in the previous sections.
Because React relies on heuristics, if the assumptions behind them are not met, performance will suffer.
-
Keys should be stable, predictable, and unique. Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.
-
Keys should be unique among siblings. Duplicate keys will cause React to use the item’s index as a last resort when it can’t find a key. This can cause issues with component state and lifecycle methods.
-
Keys should be stable over time. If the key changes between renders, React will treat the old and new components as completely different components. This can cause state to be lost and components to be unmounted and remounted.
Conclusion
React is a powerful and efficient front-end library that has revolutionized the way we build web applications. By using a virtual DOM, a reconciliation process, and a diffing algorithm, React is able to update the UI quickly and efficiently. Understanding how React works under the hood can help you write more efficient and performant code.
TLDR
The reconciliation process is what makes React so fast and efficient. By only updating the parts of the real DOM that have changed, React minimizes the amount of work that the browser has to do to re-render the page. This is why React is able to update the UI so quickly and efficiently. This process of comparing the virtual DOM with the real DOM and updating the real DOM is known as the diffing algorithm.
The virtual DOM is a lightweight copy of the real DOM that React uses to keep track of the changes that need to be made to the UI. When you make changes to the UI, React updates the virtual DOM instead of the real DOM. It then compares the virtual DOM with the real DOM and only makes the necessary changes to the real DOM. This is what makes React so fast and efficient.
The diffing algorithm is the process by which React updates the real DOM based on changes to the virtual DOM. React takes a top-down approach and compares the two trees node by node. React uses three rules to determine how to update the real DOM based on changes to the virtual DOM: components of different types, DOM elements of different types, and keyed elements.