DEV Community

Yogesh Bamanier
Yogesh Bamanier

Posted on

🚀 Demystifying React Fiber: A Guide to Incremental & Concurrent Rendering ⚛️

An in-depth guide on React Fiber, exploring WIP, commit phase, incremental and concurrent rendering, multiple WIP trees, and hooks management.


🌟 Introduction

When I started digging into React Fiber, I had endless questions swirling in my head:

  • When is the WIP (work-in-progress) Fiber created?
  • If a child’s state updates, why does React rebuild from the parent?
  • What happens if half of the work is done and React decides to pause?
  • Can multiple WIP trees exist at the same time?

This article is a knowledge base + story of my journey understanding Fiber. I’ll explain concepts in sequence while weaving in the exact questions that drove my curiosity.


🧵 The First Spark — When is Fiber Created?

When you run:

import { createRoot } from "react-dom/client";

createRoot(container).render(<App />);
Enter fullscreen mode Exit fullscreen mode
  • React creates a Root Fiber (HostRootFiber).
  • From this root, React builds the current Fiber tree (the DOM your app shows).
  • At the same time, whenever a render is triggered, React starts building a WIP Fiber tree (the potential next UI).

👉 Only one root Fiber exists, but it can have two children:

  • current → what’s painted on screen.
  • workInProgress → being built in memory for the next paint.

🔄 The WIP Story

My Question:

Does React build WIP after each commit? If yes, how does it know about pending work?

Answer:

  • After every commit, the WIP becomes the new current.
  • If updates are queued while rendering, React either:

    • Reuses partial WIP work (resume rendering).
    • Discards half-done WIP and starts a fresh one for better responsiveness.

💡 Even if React discards a WIP, your updates aren’t lost — they live in the update queue of the current Fiber.


⏸️ Pausing, Resuming, and Discarding

React Fiber was built to support interruptible rendering.

  • If work takes too long, React will yield (pause) to let the browser paint.
  • On the next frame, React can:

    • Resume the same WIP (continue where it left).
    • Discard WIP and rebuild from current (if higher-priority update arrived).

📌 Example:
If you typed into <input />, React might throw away half-done WIP and rebuild quickly so your keystroke feels instant.


🌳 Parent–Child Fiber Reconciliation

My Question:

If only child1 state changes, why does React traverse from root → parent → child1 → child2?

Answer:

Because reconciliation always starts from the parent of the updated Fiber.

  • Parent’s WIP Fiber is rebuilt.
  • Children are visited to decide:

    • Reuse existing Fiber (alternate).
    • Or create new WIP Fiber (if props/state changed).

👉 This is why even siblings (like child2) may be traversed, though not always rebuilt.


⚡ Commit Phase — Expanded View

I initially thought commit = “DOM changes are applied”, but it’s much richer.

Commit has three steps:

  1. Before Mutation (snapshot phase)
  • getSnapshotBeforeUpdate
  • React prepares, e.g., measuring scroll position.
  1. Mutation Phase (sync, cannot be interrupted 🚨)
  • DOM nodes are created/updated/removed.
  • Example: inserting all ready child DOM nodes in a single batch for performance.
  1. Layout Phase (effects)
  • Runs lifecycle hooks:

    • componentDidMount
    • componentDidUpdate
  • Runs passive effects scheduling.

👉 Only after this, the browser paints with the new UI.


🎭 Multiple WIP Trees

Here’s the part that really blew my mind:

  • React can prepare multiple versions of the UI simultaneously, but only in between commit waves.
  • At any given moment:

    • current → the UI you see.
    • One or more WIP candidates in memory.

⚖️ React chooses the winning WIP at commit time. Others are discarded.

📌 Example Flow:

  • Frame 1: WIP Slice A (30 Fibers). Yield.
  • Frame 2: Higher-priority update arrives. React may abandon Slice A.
  • Frame 3: New WIP tree built, committed, and painted.

So yes — multiple WIPs can exist between commits, but only one reaches the DOM.


🧩 Incremental Rendering vs Concurrent Rendering

I used to mix these two. Here’s the difference:

Incremental Rendering (Time Slicing)

  • Breaks a single render into smaller chunks (slices)
  • Each slice is executed for a small duration (~5ms-16ms), then yields control to the browser
  • Keeps the UI responsive during heavy renders

Example:

function App() {
  return (
    <>
      <HeavyList />   // takes long to render
      <Sidebar />     // light component
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • React renders Sidebar in the first slice, then starts HeavyList
  • If a user clicks a button during HeavyList rendering, React can yield to handle input immediately

Concurrent Rendering

  • React can prepare multiple WIP trees simultaneously for the same root
  • Enables high-priority updates to interrupt low-priority work
  • WIP trees are in memory; only one is committed to the DOM

Example:

<App>
  <SearchBar />      // high priority
  <Feed />           // low priority, heavy list
</App>
Enter fullscreen mode Exit fullscreen mode
  • Typing in SearchBar triggers a high-priority WIP tree
  • React may pause Feed WIP, build SearchBar update concurrently
  • The most appropriate WIP tree commits first

Key Difference:

  • Incremental = split one render into chunks
  • Concurrent = multiple possible renders at the same time, React decides which to commit

🕒 Timeline Comparison

Incremental Rendering

Type "a"
 -> Render Input Fiber
 -> Pause
 -> Render List Fiber (chunked)
 -> Commit everything
Enter fullscreen mode Exit fullscreen mode

Concurrent Rendering

Type "a"
 -> Commit Input Fiber immediately
 -> Start List Fiber in background

Type "ab" before List finishes
 -> Discard old List Fiber
 -> Start new List Fiber with "ab"
 -> Commit when ready
Enter fullscreen mode Exit fullscreen mode

This timeline shows how incremental rendering splits work in one render while concurrent rendering handles multiple WIPs and high-priority interruptions.


📖 Key Takeaways

  • Fiber enables time-sliced, interruptible rendering.
  • WIP is always built in memory; DOM only updates during commit.
  • React may pause, resume, or discard WIP based on priority.
  • Multiple WIP trees can exist between commit waves, but only one survives.
  • Commit Phase has 3 sub-phases (before mutation, mutation, layout).
  • Update queues ensure no user input is lost, even if WIP is thrown away.

✍️ Author

👨‍💻 Yogesh Bamanier
🔗 LinkedIn

Top comments (2)

Collapse
 
prime_1 profile image
Roshan Sharma

Nice post! Your breakdown of React Fiber's WIP and commit phases is super insightful. The way you explain how React builds and commits updates in phases really clarifies the process. It's fascinating to see how multiple WIP trees can exist and how React manages them to ensure smooth updates. This deep dive into concurrent rendering and hooks management adds a lot of value for developers looking to understand React's internals better.

Collapse
 
yorgie7 profile image
Yogesh Bamanier

Thank You so much