Skip to main content

React Fiber Architecture

Difficulty: 🔴 Hard | Frequency: ⭐⭐⭐

Why Fiber Was Created

The original stack reconciler processed updates synchronously without interruption. Once React began rendering, it had to complete the entire tree before returning control to the browser. Problems:

  • UI blocking: Large updates prevented input handling
  • Dropped frames: Complex renders caused animations to stutter
  • No prioritization: Critical updates couldn't interrupt background work
  • Mobile: Limited CPU made blocking worse

What Fiber Is

Every React element corresponds to a Fiber node — a JavaScript object:

// Fiber node (simplified)
{
type: ComponentFunction, // Component or DOM element type
key: null,
child: Fiber | null, // First child
sibling: Fiber | null, // Next sibling
return: Fiber | null, // Parent
memoizedState: Hook | null, // Hook state linked list
memoizedProps: Props,
flags: number, // What DOM operation is needed
lanes: Lanes, // Update priority
alternate: Fiber | null, // Double buffer (current vs work-in-progress)
}

The tree structure allows traversal without recursion — React walks child → sibling → parent, enabling pausing at any point.

Two-Phase Rendering

Render Phase (Interruptible)

  • Reconciles component changes
  • Calls component functions and lifecycle methods
  • Creates/updates fiber nodes
  • Can be paused, resumed, or aborted
  • No DOM changes happen here

Commit Phase (Synchronous)

  • Applies all DOM changes at once
  • Fires useLayoutEffect (before paint)
  • Fires useEffect (after paint)
  • Cannot be interrupted — always completes once started

Time-Slicing

React performs work in ~5ms chunks. Between chunks, it checks if higher-priority work arrived:

Render chunk 1 (5ms) → Check: high priority work?
No → Render chunk 2 (5ms) → Check: high priority work?
Yes → Pause, handle urgent work → Resume

This maintains 60 FPS (16.67ms per frame) even during heavy rendering.

Priority Lanes (React 18)

Updates are assigned priority:

PriorityExample
Discrete (highest)Click, keypress, focus
ContinuousDrag, mouse move, scroll
DefaultData fetching, transitions
Idle (lowest)Analytics, prefetching

High-priority updates interrupt lower-priority work in progress.

Double Buffering

React maintains two fiber trees:

  • current — the tree currently rendered on screen
  • workInProgress — the tree being built for the next render

After commit, workInProgress becomes current. This prevents showing partially-rendered UI.

Concurrent Features Built on Fiber

// useTransition: explicitly mark non-urgent updates
function SearchPage() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();

const handleChange = (e) => {
setQuery(e.target.value); // Urgent: immediate

startTransition(() => {
setResults(filter(allItems, e.target.value)); // Non-urgent: deferrable
});
};

return (
<>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />}
<Results items={results} />
</>
);
}

// useDeferredValue: auto-deferred copy of a value
function App({ data }) {
const deferredData = useDeferredValue(data);
return <ExpensiveChart data={deferredData} />;
}

Suspense Integration

Fiber enables components to "suspend" — throw a promise — and React catches it at the nearest <Suspense> boundary:

<Suspense fallback={<Loading />}>
<DataComponent /> {/* Can suspend while loading */}
</Suspense>

React pauses rendering the suspended component, shows the fallback, and resumes when the promise resolves.

Performance Characteristics

ConcernBefore FiberAfter Fiber
Large list filterFreezes input (500ms+)Input responsive (0ms)
Slow network renderBlank screen until doneProgressive content display
Animation during updateDropped framesSmooth 60fps
PrioritizationNoneTime-slicing + lanes

Fiber overhead: ~3KB additional bundle size. Justified when renders exceed ~50ms.


Content from Frontend-Master-Prep-Series03-react