Back to React
React
hard
mid

What is React Fiber and how does it improve rendering performance?

Fiber is React 16's rewrite of the reconciler that splits rendering work into small units of work (fibers), each interruptible. The scheduler can pause a render, yield to the browser for high-priority work (input, animation), then resume — keeping the main thread responsive. Enables concurrent features (useTransition, Suspense, time-slicing). Pre-Fiber React (Stack reconciler) was synchronous and blocked the main thread on big trees.

7 min read·~5 min to think through

Fiber is the rewritten internal architecture of React's reconciler, shipped in React 16.

Before Fiber: Stack reconciler

React 15 walked the component tree recursively in a single synchronous pass. Once started, a render couldn't be interrupted. For big trees:

  • The main thread blocked until the entire tree was reconciled.
  • Input events queued behind the render.
  • Frame drops and jank.

After Fiber: incremental reconciler

The tree is represented as a linked list of fiber nodes — one per component instance. Reconciliation iterates the list in chunks. Between chunks, the scheduler can yield to the browser.

ts
Old (stack):   [render entire tree] → commit
New (fiber):   [unit] [unit] [unit] → yield → [unit] [unit] → commit

Two phases

1. Render phase (interruptible):

  • React walks fibers, builds the new tree (work-in-progress).
  • Can pause, abandon, or resume.
  • No DOM mutations yet.

2. Commit phase (synchronous):

  • React applies all DOM changes in one synchronous pass.
  • Runs lifecycle methods, useLayoutEffect.
  • Schedules useEffect for after paint.

The split is critical: the render phase can be redone or thrown away; only the commit actually touches the DOM.

What a fiber looks like

ts
{
  type: Component,
  stateNode: instance | DOM node,
  child, sibling, return, // tree links
  pendingProps, memoizedProps,
  memoizedState,           // hook state
  effectTag,               // what to do at commit
  alternate,               // the other fiber tree (current vs work-in-progress)
}

Performance improvements

  1. Time slicing: long renders don't block input. The scheduler yields every ~5ms.
  2. Priority lanes: urgent updates (input) jump ahead of non-urgent (data refresh).
  3. Suspense: tree can be left 'pending' while data loads.
  4. Concurrent features: useTransition, useDeferredValue, Server Components.

Concurrent rendering example

tsx
const [pending, startTransition] = useTransition();

function onInput(q: string) {
  setQuery(q);                      // urgent — must commit immediately
  startTransition(() => {
    setResults(filter(q));          // non-urgent — interruptible
  });
}

The transition render can be discarded if a new keystroke arrives — no wasted DOM mutations.

Double-buffering

React keeps two fiber trees:

  • current: what's on screen.
  • work-in-progress (WIP): being built.

After commit, WIP becomes current and current becomes WIP for the next render. This makes pause/resume safe.

Diffing algorithm — unchanged in spirit

Fiber didn't change the O(n) heuristic diff:

  • Same type → reuse, diff children.
  • Different type → unmount and remount.
  • Keys → match list children across renders.

But it made the diff yieldable, which is the real win.

What developers see

Most app code doesn't care that Fiber exists — useState, useEffect, JSX all behave the same. Fiber matters because:

  • Concurrent features now work.
  • The scheduler can prioritize.
  • Big trees don't drop frames.

Limitations

  • Class component lifecycle methods that didn't account for interruption became 'unsafe' (componentWillMount, componentWillReceiveProps, componentWillUpdate).
  • Side effects in the render phase are now bugs — React may call your component multiple times for the same commit.

Senior framing

Fiber's value isn't 'faster individual renders' — it's 'renders that yield to the browser'. The reconciler becomes a scheduler. That's what unlocks every concurrent feature React has shipped since 18, from Suspense to Server Components.

Follow-up questions

  • What's the difference between render phase and commit phase?
  • Why are componentWillMount and componentWillReceiveProps unsafe under Fiber?
  • How does Fiber enable Suspense?

Common mistakes

  • Side effects in the render phase — Fiber may call your component multiple times.
  • Assuming Fiber makes individual renders faster — it makes them yieldable.
  • Mutating refs during render — defeats the work-in-progress safety.

Performance considerations

  • Time-slicing keeps frame rate up even on heavy renders. Per-render CPU cost is similar to pre-Fiber. The win is jank reduction, not throughput.

Edge cases

  • Synchronous APIs (flushSync) force the render to complete without yielding.
  • useLayoutEffect runs synchronously between commit and paint, blocking the browser.
  • Long render tasks still block; Fiber yields every 5ms but a single fiber's work isn't subdivided.

Real-world examples

  • Every React 16+ app uses Fiber. The user-visible benefits showed up with React 18's concurrent features (transitions, deferredValue). Apps with heavy renders feel dramatically smoother.

Senior engineer discussion

Senior framing: Fiber is the foundational architecture. The visible features (Suspense, transitions, RSC) are all downstream of 'the reconciler is now a scheduler that yields'. Understanding that explains why React 18's API exists and where it's going.

Related questions