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 />);
- 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).
- Reuse existing Fiber (
๐ 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:
- Before Mutation (snapshot phase)
getSnapshotBeforeUpdate
- React prepares, e.g., measuring scroll position.
- 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.
- 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 </> ); }
- React renders
Sidebar
in the first slice, then startsHeavyList
- 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>
- Typing in
SearchBar
triggers a high-priority WIP tree - React may pause
Feed
WIP, buildSearchBar
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
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
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 (0)