6 min read
0%

React Reconciliation Algorithm

Back to Blog
React Reconciliation Algorithm

React Reconciliation Algorithm

React’s reconciliation algorithm determines what changed in the component tree and updates only those parts of the DOM. Understanding it helps you write faster apps and avoid subtle bugs.

The Core Problem

Re-rendering a component returns a new tree of React elements. Comparing two arbitrary trees has O(n³) complexity. React gets this down to O(n) by making two heuristics:

  1. Elements of different types produce completely different trees
  2. Keys hint which list items are stable across renders

Element Type Diffing

When React compares two nodes:

// Before
<div className="card">
  <UserAvatar />
</div>

// After
<section className="card">
  <UserAvatar />
</section>

Different type (divsection) — React unmounts the entire subtree and mounts fresh. UserAvatar loses its state even though nothing about it changed.

Same type — React keeps the DOM node and only updates changed props.

// Before
<input type="text" value="hello" />

// After
<input type="text" value="world" />

React updates value in place. The DOM node survives.

Component Reconciliation

For component nodes, same type means React calls the new render with updated props and reconciles the output recursively. State is preserved.

function App({ page }) {
  return page === 'home' ? <HomePage /> : <SettingsPage />;
}

Switching page between 'home' and 'settings' unmounts and remounts because the types differ. If both were the same component (<Page type={page} />), state would survive.

List Reconciliation and Keys

Without keys, React reconciles lists by position:

// Before: [A, B, C]
// After:  [X, A, B, C]  (X inserted at front)

React sees: position 0 changed (A→X), position 1 changed (B→A), etc. All four nodes get patched.

With keys, React matches by identity:

{items.map(item => (
  <Item key={item.id} data={item} />
))}

React finds the key, moves the existing DOM node, and only mounts X. O(n) instead of O(n²).

Key rules:

  • Keys must be stable across renders (no Math.random())
  • Keys must be unique among siblings, not globally
  • Use database IDs, not array indexes (unless the list never reorders)

When Index Keys Cause Problems

// Dangerous for reorderable lists
{items.map((item, i) => (
  <input key={i} defaultValue={item.name} />
))}

If you prepend an item, the first <input> now has key 0 which was already associated with the old first item’s DOM node. React reuses it — the uncontrolled input value doesn’t update.

Reconciliation vs Rendering

Reconciliation is not rendering. Rendering is calling your component function to get a React element tree. Reconciliation is React’s comparison of old and new trees. You can trigger renders that produce identical trees — reconciliation sees no changes and DOM stays untouched.

React.memo, useMemo, and shouldComponentUpdate short-circuit rendering, but they also skip reconciliation for that subtree.

Practical Implications

// Bad: creates a new component type on every render
function Parent() {
  const Child = () => <div>hello</div>; // defined inside
  return <Child />;
}

Every render of Parent creates a new function reference. React sees a new type at that position and unmounts/remounts the entire subtree. Never define components inside render.

// Good: stable reference
const Child = () => <div>hello</div>;

function Parent() {
  return <Child />;
}

Canvas is not supported in your browser