Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
ReactNext.jsJune 24, 20264 min read

React reconciliation explained: How to optimize your DOM updates

React reconciliation is the engine behind your UI updates. Learn how DOM diffing works and how to minimize mutations to keep your React app fast.

ReactFrontendPerformanceJavaScriptWeb DevelopmentNext.jsTutorial

Last month, I spent about three days debugging a sluggish dashboard. Every time a user typed in a search input, the entire list of 500 items re-rendered, causing a noticeable delay of roughly 120ms per keystroke. The culprit wasn't the logic itself, but a misunderstanding of how React handles updates.

To write performant code, you have to stop thinking about "updating the DOM" and start thinking about how React manages its internal tree.

Understanding React Reconciliation and DOM Diffing

At its core, React reconciliation is the process where React updates the DOM to match the latest version of your component tree. When you call setState or pass new props, React doesn't just blast the entire DOM with new HTML. That would be incredibly expensive.

Instead, it creates a new "Virtual DOM" tree and compares it to the previous one. This is the DOM diffing phase. React uses a heuristic algorithm that operates in $O(n)$ time. It assumes two things:

  1. Two elements of different types will produce different trees.
  2. The developer can hint at which child elements may be stable across renders with a key prop.

If the root element type changes—say, from a <div> to a <span>—React throws out the old tree and builds a new one from scratch. If the type stays the same, it only updates the attributes that changed. This is where most performance gains happen.

The Wrong Turn: Why Keys Matter

I once worked with a junior dev who used array indices as key values in a dynamic list. It seemed fine until we started deleting items from the middle of the list. Because the indices shifted, React got confused about which component instance belonged to which data.

We saw the state of the third item jump to the second item, and the browser repainted the entire list. It was a mess.

If you want to master this, you should look into how React reconciliation and component state persistence: A mental model dictates how your UI holds onto data. When you use stable IDs—like a database primary key—instead of indices, you allow React to move elements around rather than destroying and recreating them.

Minimizing DOM Mutations

You can't optimize what you don't track. To minimize mutations, you need to be surgical about where state lives. If a parent component re-renders, all its children re-render by default. This is often harmless, but when you have deep trees, it adds up.

Here is a quick pattern I use to keep things lean:

  1. Lift state down: Keep state as close to the component that needs it as possible.
  2. Memoize heavy components: Use React.memo for expensive components that don't need to re-render unless their props actually change.
  3. Avoid unnecessary nesting: Every wrapper component is another node in the tree that React has to traverse during React virtual DOM diffing.

If you're dealing with complex UI, understanding React rendering and layout shifts: A guide to stable UIs will help you prevent the browser from doing extra work during those paint cycles.

The Reality of React Performance Optimization

People often obsess over micro-optimizations, but usually, the bottleneck is just unnecessary re-renders. Before you reach for useMemo or useCallback, check your component structure.

Are you passing an object literal as a prop?

JAVASCRIPT
// Bad: creates a new object on every render
<ExpensiveComponent config={{ theme: CE9178">'dark' }} />

// Good: memoize the object outside the component or use useMemo
const config = useMemo(() => ({ theme: CE9178">'dark' }), []);
<ExpensiveComponent config={config} />

When you pass an object literal, the reference changes every time the parent renders. Even if the content is identical, React thinks the props have changed and triggers a re-render. This breaks the shallow comparison that React.memo relies on.

Frequently Asked Questions

Does Virtual DOM mean the DOM isn't updated? No. The Virtual DOM is just a lightweight JavaScript object representation. React still updates the real DOM, but it does so only after calculating the absolute minimum set of changes required.

Is reconciliation the same as rendering? Not exactly. Rendering is the process of calling your components to get the new UI tree. Reconciliation is the process of comparing that new tree to the old one to decide what to change in the actual DOM.

Should I use key={Math.random()}? Never. This forces React to destroy and recreate the component on every single render. It kills performance and wipes out any local state, like input values or scroll positions.

Moving Forward

I’m still experimenting with how to better profile these re-renders in production environments. Using the React DevTools Profiler is great for local testing, but catching these issues in the wild is harder.

Next time you're building a feature, try to visualize the tree. Ask yourself: "If this state updates, which nodes are actually forced to re-evaluate?" If you can answer that, you’re already ahead of most engineers. Don't worry about getting it perfect on the first pass—just aim to keep your component tree as shallow and stable as possible.

Back to Blog

Similar Posts

ReactNext.jsJune 24, 20263 min read

React form handling: Controlled vs. Uncontrolled Components

React form handling doesn't have to be complex. Learn the trade-offs between controlled and uncontrolled components to decide when to sync your UI state.

Read more
ReactNext.jsJune 24, 2026
4 min read

React performance: When to use useMemo and useCallback

React performance depends on knowing when to memoize. Learn the mental model for using useMemo and useCallback effectively without falling into optimization traps.

Read more
ReactNext.jsJune 22, 20264 min read

React derived state: Stop using useEffect for data calculations

React derived state is the key to faster components. Learn how to stop abusing useEffect for data transformations and simplify your React performance optimization.

Read more