When you write React code, you usually just think in terms of state → UI.
But have you ever wondered how React efficiently updates the UI without repainting everything?
The secret sauce is Reconciliation, Fiber, and the Virtual DOM.
Let’s break it down step by step 👇
🧠 1. Virtual DOM: The Memory Blueprint
React doesn’t touch the real DOM directly every time you update state.
Instead, it builds a Virtual DOM — a lightweight JS object tree representing your UI.
Example UI:
<div id="app">
<h1>Hello</h1>
<button>Click</button>
</div>
Virtual DOM Representation:
{
type: "div",
props: { id: "app" },
children: [
{ type: "h1", props: {}, children: ["Hello"] },
{ type: "button", props: {}, children: ["Click"] }
]
}
Each node has:
-
type
→ element/component type -
props
→ attributes -
children
→ nested nodes or text
Why it’s faster:
Updating JS objects in memory is much cheaper than updating the real DOM, which triggers:
- style recalculation
- layout reflow
- repainting pixels
⚡ 2. React Diffing and Minimal DOM Updates
When you call setState
, React doesn’t immediately touch the DOM.
It follows these steps:
- Builds a new Virtual DOM.
- Compares it with the old Virtual DOM (diffing).
- Generates a patch with only the differences.
Example:
<!-- Before -->
<button>Click</button>
<!-- After -->
<button>Submit</button>
React sees only the text changed and updates button.textContent
in the DOM.
The DOM is dumb — it just follows commands.
✅ You could manually update the DOM, but in complex apps that quickly becomes unmanageable.
🧩 3. Batching Updates
React groups multiple updates together in a process called batching.
This prevents unnecessary re-renders.
Pre-React 18:
Batching happened only inside React event handlers.
Example:
function handleClick() {
setCount(c => c + 1);
setName("Alex");
}
Both updates happen together → one render
React 18+ (Automatic Batching):
Now, batching works everywhere, even inside async code.
Example:
setTimeout(() => {
setCount(c => c + 1);
setName("Alex");
}, 0);
Both updates inside the timeout are batched → one render, not two.
Bottom line: fewer renders → smoother UI.
🧵 4. Fiber: Making Large Trees Responsive
For huge UI trees, diffing everything at once can freeze the page.
React Fiber fixes that.
How Fiber works:
- Breaks work into units: Each component is a “unit of work.”
- Incremental rendering: React processes a few nodes, pauses, then continues.
- Yielding: React temporarily pauses work so the browser can handle input, scrolling, or animations.
-
Priority system:
- High: typing, animations
- Low: timers, transitions (
startTransition()
for low-priority updates)
💡 Analogy:
Old React = lifting 1000 kg of UI in one go → UI freezes.
Fiber = lifting 50 kg, take a break, continue → smooth experience.
🚀 5. Virtual DOM Skip / Compile Updates Directly
Some frameworks like Svelte or Solid go beyond React’s Virtual DOM idea.
They skip the diffing step entirely.
They:
- Track exactly which state affects which node
- Compile your code to direct DOM updates
Example:
function updateCount(newCount) {
button.textContent = newCount;
}
No runtime diffing → direct DOM manipulation → blazing fast.
🧾 Key Takeaways
- Virtual DOM → memory-efficient blueprint of the UI
- Reconciliation → minimal DOM updates after diffing
- Batching → multiple state updates grouped into one render
- Fiber → incremental rendering, yielding, and priority control
- Virtual DOM skip / compile updates → direct DOM updates for peak performance
React may seem like magic, but now you know the mechanics behind its efficiency.
Understanding this helps you write faster, smoother, and more predictable React apps.
💬 Did this make React’s internals a bit clearer?
Drop a comment — I’d love to know which part clicked for you!
Top comments (0)