๐Ÿณ IntuitiveFE
Login
โ† All concepts

Fiber & Concurrent Rendering

โฑ๏ธ ~4-minute bite ยท solve the sandbox to master

0%lesson
๐Ÿง’

5-Year-Old Metaphor

โ€” The physical, real-world picture. No jargon.

๐Ÿณ A restaurant kitchen with priority lanes โ€” urgent orders jump the queue, prep work pauses for VIP tables.

The two-phase model

React rendering is split into two fundamentally different phases. The render phase is pure โ€” it builds a description of what the DOM should look like. It can be interrupted, thrown away, and restarted as many times as needed. The commit phase applies the changes to the real DOM. It runs to completion without interruption because a half-applied DOM update would leave the UI in an inconsistent visible state.

Render phase (interruptible)

  • โ€ข Calls your component functions
  • โ€ข Diffs the fiber tree
  • โ€ข Computes what changed
  • โ€ข Can be paused, resumed, discarded
  • โ€ข MUST be side-effect free

Commit phase (always synchronous)

  • โ€ข Applies DOM mutations
  • โ€ข Runs useLayoutEffect
  • โ€ข Updates refs
  • โ€ข Runs useEffect (async)
  • โ€ข Always runs to completion

Priority lanes (React 18)

React 18 introduced a lane-based priority system with 16 lanes. Higher-priority lanes preempt lower ones. Common lanes:

SyncLaneUser input (click, keypress)
InputContinuousLaneDrag, scroll
DefaultLaneNormal setState updates
TransitionLanestartTransition updates
IdleLaneOffscreen/prefetch work

The Fiber data structure

ts
1// Simplified Fiber node (~30 real fields)
2interface Fiber {
3 type: Function | string; // component fn or 'div'
4 stateNode: Element | null; // real DOM node or class instance
5 child: Fiber | null; // first child
6 sibling: Fiber | null; // next sibling
7 return: Fiber | null; // parent fiber
8 pendingProps: Props;
9 memoizedProps: Props; // props from last render
10 memoizedState: Hook | null;// hook linked list
11 lanes: Lanes; // pending work priority
12 flags: Flags; // what changed (Placement, Update, etc.)
13 alternate: Fiber | null; // the "other" tree (double buffering)
14}

React maintains two trees: the current tree (what's in the DOM) and the work-in-progress tree being built. On commit, the trees swap. This is called double buffering โ€” the same technique graphics cards use.

๐ŸŽ›๏ธ

Interactive Sandbox

โ€” Move something, see it react instantly.

Rendering strategy

Legacy synchronous rendering โ€” entire tree processed in one uninterruptible blocking call. Browser cannot run input handlers, animations, or any other work until React finishes.

Work loop timeline

renderprocess App fiber8ms
renderprocess Header fiber12ms
renderprocess ProductList fiber (500 items)40ms
renderdiff vdom tree15ms
commitapply DOM mutations5ms

render starts โ€” browser is now blocked

render
commit
pause
resume
Gotcha: A long synchronous render blocks the main thread. Users clicking during this window get no feedback โ€” their events are queued and processed only after React finishes.
Insight: React 16 introduced Fiber as a linked-list work unit so that rendering could be broken into pausable chunks. Legacy sync mode still exists for backward compatibility.
Explored:๐Ÿงฑ๐Ÿ”—โšก๐Ÿ”„๐Ÿš€
๐ŸŽฏ

Challenge

Explore all 5 rendering patterns. Notice the difference between render phases (pausable) and commit phases (always synchronous). Why can render be interrupted but commit cannot?

Try it
๐ŸŽฏ

Why Should I Care?

โ€” The exact interview question + the bug it kills.

Exact interview questions

Q: Why can render be interrupted but commit cannot?

Render is pure โ€” it only builds a virtual description. If interrupted, React can discard the partial work and restart without side effects visible to users. Commit applies DOM mutations which are immediately visible. A partial commit would show inconsistent UI โ€” e.g., a button updated but its parent not yet. React runs commit synchronously to guarantee the DOM is always in a consistent state after each update.

Q: What does startTransition do exactly?

js
1import { startTransition, useTransition } from 'react';
2ย 
3// Method 1: startTransition wrapper
4startTransition(() => {
5 setSearchResults(filter(data, query)); // non-urgent
6});
7ย 
8// Method 2: useTransition hook
9const [isPending, startTransition] = useTransition();
10<button onClick={() => startTransition(() => setTab('heavy'))}>
11 {isPending ? <Spinner /> : 'Heavy Tab'}
12</button>

startTransition marks updates inside it as "transition" priority (lower than user input). React can interrupt them if something more urgent arrives. The key benefit: the current UI stays interactive while the transition is computing.

Q: What is "tearing" in Concurrent Mode?

Tearing occurs when different components read different values from the same external store during a single render pass. In Concurrent Mode, renders can be interleaved โ€” a store update can happen between two component renders in the same tree, causing visual inconsistency. Use useSyncExternalStore (React 18) when integrating external stores like Redux or Zustand to prevent this.

๐Ÿ”ฌ

The Deep Dive

โ€” Spec refs, engine internals, the minutiae.

The work loop algorithm

ts
1// Simplified React work loop (packages/react-reconciler)
2function workLoopConcurrent() {
3 while (workInProgress !== null && !shouldYield()) {
4 performUnitOfWork(workInProgress);
5 }
6}
7ย 
8function shouldYield(): boolean {
9 // Scheduler checks remaining time in current 5ms frame
10 return getCurrentTime() >= deadline;
11}
12ย 
13function performUnitOfWork(fiber: Fiber) {
14 // 1. "Begin" phase: call component, reconcile children
15 const next = beginWork(fiber);
16 if (next === null) {
17 // no children โ€” "complete" this fiber, walk up
18 completeUnitOfWork(fiber);
19 } else {
20 workInProgress = next; // descend to first child
21 }
22}

Scheduler package โ€” time-slicing

React's scheduler uses MessageChannel (not setTimeout) to schedule work after the current task. MessageChannel fires in the macrotask queue but before paint, giving React a chance to do work every ~5ms without blocking the browser's rendering pipeline.

js
1// Scheduler internals (simplified)
2const channel = new MessageChannel();
3channel.port2.onmessage = performWorkUntilDeadline;
4ย 
5function scheduleWork(callback) {
6 channel.port1.postMessage(null); // yields to browser, then resumes
7}
8ย 
9function performWorkUntilDeadline() {
10 deadline = getCurrentTime() + 5; // 5ms time slice
11 workLoop(); // runs until deadline or work exhausted
12}

Streaming SSR + Selective Hydration (React 18)

With renderToPipeableStream, React can stream HTML chunks to the browser as they're ready. When the client receives HTML, it can start hydrating high-priority components (e.g., the nav) before the rest of the page arrives. User interactions during hydration are handled via event capture โ€” React replays them once the relevant component hydrates.

js
1// Server: streaming with Suspense
2import { renderToPipeableStream } from 'react-dom/server';
3ย 
4const { pipe } = renderToPipeableStream(<App />, {
5 bootstrapScripts: ['/main.js'],
6 onShellReady() {
7 res.setHeader('content-type', 'text/html');
8 pipe(res); // stream HTML as chunks resolve
9 },
10});
11ย 
12// Client: selective hydration
13import { hydrateRoot } from 'react-dom/client';
14hydrateRoot(document, <App />);
15// React hydrates high-priority components first
๐ŸŽค

Interview Questions

โ€” Real questions from real interviews โ€” with answers.

A Fiber is a plain JS work unit representing one component; the old reconciler used the call stack and couldn't be paused.

Render is pure and discardable; commit writes real DOM mutations that are immediately visible to users.

It marks state updates as non-urgent so React can interrupt them if higher-priority work (user input) arrives.

Time-slicing breaks render work into ~5ms chunks yielded to the browser via MessageChannel.

React keeps two fiber trees โ€” the current (committed) tree and the work-in-progress tree being built โ€” and swaps them atomically on commit.

Lanes are bit-mask priorities; SyncLane = user input (highest), TransitionLane = startTransition work, IdleLane = background prefetch (lowest).

๐ŸŽฎ

Memory Game

โ€” Quick quiz โ€” lock the concept in long-term memory.
1/4

How does React's double-buffering differ from how V8 garbage collects objects?