
React's Reconciliation Algorithm Explained

One of React's biggest strengths is its ability to update the UI efficiently. Instead of re‑rendering the entire page every time something changes, React compares the new state with the previous one and only updates what is necessary. This process, known as reconciliation, ensures smooth, fast updates without unnecessary work.
But how does React decide which parts of the UI need updating? That's where the reconciliation algorithm comes in. In this article, I explore how it works, why the virtual DOM is important, and what we can do to optimise rendering performance.
What is the Virtual DOM?
Before looking at reconciliation, we need to understand the virtual DOM (VDOM) and why React uses it.
When we update state in React, it does not modify the real DOM immediately. Instead, React updates a virtual representation of the DOM. This allows React to:
Batch updates efficiently
– Rather than making multiple small changes, React groups them together to improve performance.Minimise DOM manipulation
– The real DOM is slow to update, so React only modifies what is necessary.
By using the virtual DOM, React can determine precisely what has changed before touching the real DOM, keeping updates as fast as possible.
How React's Reconciliation Algorithm Works
When a component's state or props change, React follows these steps to update the UI efficiently:
1. Generate a New Virtual DOM
React first creates a new virtual DOM tree that reflects the latest state.
2. Compare the New and Old Virtual DOM (Diffing)
React then compares the new virtual DOM with the previous one. Instead of re‑rendering everything, it finds the differences (this process is called "diffing").
For example, consider this simple component:
const ExampleComponent = ({ title }: { title: string }) => { return <h1>{title}</h1>;};If title changes from "Hello" to "Welcome", React does not replace the entire <h1> element. It only updates the text inside it, keeping the process efficient.
3. Update the Real DOM
After identifying what has changed, React updates only the affected elements in the real DOM. This keeps updates fast and smooth.
How React Determines What to Update
React follows two key rules to optimise how elements are updated:
1. Elements of Different Types Trigger a Full Re‑Render
If an element's type changes, React does not try to update it, it destroys the old element and creates a new one.
const Component = ({ isHeading }: { isHeading: boolean }) => isHeading ? <h1>Hello</h1> : <p>Hello</p>;Since <p> and <h1> are different element types, React removes the <p> and replaces it with <h1>, instead of just changing the text.
2. Keys Optimise List Updates
When rendering lists, React uses keys to track items between renders. Without unique keys, React might remove and recreate items rather than reordering them efficiently.
Inefficient Rendering (No Keys):
const List = ({ items }: { items: string[] }) => ( <ul> {items.map((item) => ( <li>{item}</li> // No key provided ))} </ul>);React may treat each <li> as a new element, leading to unnecessary re‑renders.
Optimised Rendering (Using Keys):
const List = ({ items }: { items: string[] }) => ( <ul> {items.map((item) => ( <li key={item}>{item}</li> // Keys help React track items ))} </ul>);Adding a unique key allows React to detect which items have changed, been added, or removed, making the update process much more efficient.
Improving Performance with Reconciliation
React's reconciliation algorithm is already optimised, but we can take it a step further by following best practices.
1. Prevent Unnecessary Re‑Renders with React.memo
If a component renders the same output for the same props, we can prevent unnecessary updates using React.memo:
const ExpensiveComponent = React.memo(({ value }: { value: string }) => { console.log("Rendered"); return <p>{value}</p>;});Now, ExpensiveComponent only re‑renders if value changes.
2. Use useCallback and useMemo to Avoid Recomputations
useCallback prevents functions from being recreated unnecessarily whilst useMemo caches expensive calculations.
const handleClick = useCallback(() => { console.log("Clicked");}, []);const expensiveValue = useMemo(() => computeExpensiveResult(), []);3. Use Stable Keys in Lists
Always use unique, stable keys when rendering lists to help React track changes efficiently.
4. Avoid Inline Functions and Objects in JSX
Creating new functions inside JSX can cause unnecessary re‑renders.
Less Efficient:
<button onClick={() => console.log("Clicked")}>Click me</button>More Efficient:
const handleClick = () => console.log("Clicked");<button onClick={handleClick}>Click me</button>;Wrapping up
React's reconciliation algorithm ensures efficient UI updates by comparing the virtual DOM with the previous state and applying only the necessary changes. By understanding how this process works, we can write better‑performing applications and avoid unnecessary re‑renders.
Key Takeaways
- React compares the virtual DOM with the previous state to update only the necessary parts of the UI.
Elements of different types
are replaced rather than updated.Keys in lists
help React track and reorder items efficiently.Optimisations like
React.memo,useCallback, anduseMemohelp reduce unnecessary re‑renders.Minimising DOM updates
keeps applications fast and responsive.
By following these best practices, we can keep our React applications running smoothly and avoid performance bottlenecks as our projects grow.
Categories:
Related Articles

Finding the Median of Two Sorted Arrays with JavaScript. 
React's Virtual DOM vs. the Real DOM. React's Virtual DOM vs. the Real DOM

Understanding Arrow Functions in JavaScript. Understanding Arrow Functions in JavaScript

Building Polyfills for JavaScript Array and String Methods. Building Polyfills for JavaScript Array and String Methods

GetStaticProps vs. getServerSideProps in Next.js. getStaticPropsvs.getServerSidePropsin Next.js
Memoization in JavaScript: Optimising Function Calls. Memoization in JavaScript: Optimising Function Calls

Best Practices for Cross‑Browser Compatibility. Best Practices for Cross‑Browser Compatibility

Harnessing the Power of Prototype.bind(). Harnessing the Power of
Prototype.bind()
The Longest Palindromic Substring in JavaScript. The Longest Palindromic Substring in JavaScript

Detecting and Dealing with Website Theft. Detecting and Dealing with Website Theft

Using display in CSS. Using
displayin CSS
Solving the 'Jump Game' Problem with Greedy Algorithms. Solving the 'Jump Game' Problem with Greedy Algorithms