How does React’s reconciliation (virtual DOM diffing) actually decide what to re-render?

Flipkart Frontend Developer 3–5 Years React

React keeps a lightweight in-memory tree (the virtual DOM) describing what the UI should look like. On every state or prop change, it builds a new virtual DOM tree, diffs it against the previous one, and calculates the smallest set of real DOM operations needed to make the actual page match, then applies only those changes instead of re-rendering everything from scratch. This diffing is what makes “re-render on every state change” cheap instead of prohibitively slow.

Why it’s not a naive full-tree comparison

A generic tree-diff algorithm is expensive at scale (roughly O(n³) for n nodes), so React uses heuristics that make it O(n) in practice: it assumes elements of a different type produce entirely different trees (so it tears down and rebuilds rather than trying to reconcile a `<div>` into a `<span>`), and it uses the `key` prop to match up list items across renders instead of comparing them purely by position.

Best practice

Always give list items a stable, unique `key` tied to the actual data (an ID), not the array index, so React can correctly track which item moved, changed, or was removed instead of misattributing state to the wrong item after a reorder.

Edge case interviewers probe for

What breaks if you use the array index as the key and the list gets reordered or an item is removed from the middle? React ends up matching the wrong old element to the wrong new position, since it’s matching by index, not identity, which can cause component state (like an input’s typed text) to appear attached to the wrong row.

Common mistake

Assuming “virtual DOM” means React is always faster than direct DOM manipulation. It’s faster than naive full re-renders of a complex UI on every change, but a hand-tuned, minimal direct DOM update for a very specific case can still outperform it, the virtual DOM’s advantage is developer ergonomics plus good-enough performance by default, not an absolute speed guarantee.

What the interviewer is checking

Whether you understand reconciliation as a genuine algorithm with real heuristics and real limitations, rather than a magic phrase, and whether you know why the `key` prop actually matters instead of just knowing it’s “required.”

Imagine redecorating a room by comparing a photo of how it looks now to a photo of how you want it to look, then only moving the specific items that are different, instead of emptying the entire room and rebuilding it from nothing. React does the same thing with the webpage: it compares a “before” and “after” description of the UI and only touches the specific pieces that actually changed.

The tricky part is a list of items, like a to-do list. If React can’t tell which item is which, it might think item 3 turned into item 2 just because they swapped positions, and get confused about which one you’re actually editing. Giving each item a unique label (the `key`, like the to-do’s ID) is like putting a name tag on each item so React can always tell “this is still the same item, it just moved,” instead of guessing based on position alone.

Why interviewers ask this

It separates candidates who’ve memorized “virtual DOM makes React fast” from ones who understand the actual mechanism and its real, exploitable limitations.

What a strong answer signals

That you can explain why `key` matters concretely (identity across renders), not just that it’s a required prop React complains about in the console.

Common follow-ups

  • Why is using array index as a key discouraged?
  • How does the Fiber architecture change how reconciliation is scheduled?
  • When would you reach for `React.memo` in relation to reconciliation?

Advanced variation

“How does React Fiber let reconciliation be interrupted?” expects knowing Fiber breaks rendering work into units that can be paused, prioritized, and resumed, so a large re-render doesn’t block urgent updates like user input.

A to-do list app used the array index as each item’s `key`. Deleting an item from the middle of the list caused the wrong item’s checkbox and input text to appear to “jump” to a different row, because React matched old and new elements by position instead of by identity. Switching the key to the to-do’s actual database ID fixed it immediately, since React could now correctly track that a specific item was removed rather than assuming every item after it had shifted into a new identity.

todo-list.jsx
// Wrong: index as key breaks identity tracking when the list reorders
{todos.map((todo, index) => (
  <TodoItem key={index} todo={todo} />
))}

// Right: stable id ties each rendered element to the actual data item
{todos.map((todo) => (
  <TodoItem key={todo.id} todo={todo} />
))}
Previous tree li: Milk li: EggsNew tree li: Milk li: Breaddiff → only “Eggs” → “Bread” is patched
  1. 1React diffs a new virtual DOM tree against the previous one to compute the minimal real DOM update.
  2. 2Different element types are assumed to produce entirely different subtrees, triggering a rebuild rather than a patch.
  3. 3The `key` prop lets React track list item identity across renders instead of matching by position.
  4. 4Using array index as a key breaks correctly when items are reordered or removed from the middle.
  5. 5Fiber lets this reconciliation work be interruptible and prioritized, rather than one long blocking pass.