Say you are trying to figure out why a large form in your app freezes the browser for a fraction of a second every time a user types a character, even though nothing about the input itself looks expensive. You open the React DevTools Profiler, look at the commit, and see a wall of components re-rendering in one uninterrupted block. Someone on your team mentions that React “should” be able to break this work up and keep the page responsive. That’s Fiber they’re talking about, and understanding what it actually does — not just its name — is what separates guessing at a fix from applying one that works.

This post walks through Fiber as a series of questions, in the order most developers actually ask them once they start digging into React’s internals for performance reasons.


What Is React Fiber, in Plain Terms?

Fiber is the internal reconciliation engine React has used since version 16. It replaced the older “stack reconciler,” which processed the entire component tree synchronously, top to bottom, with no way to pause partway through. Fiber restructures that work into a linked-list-like data structure — one “fiber” node per component instance — that React can traverse incrementally, one unit of work at a time.

The practical consequence is that rendering is no longer an all-or-nothing operation. React can start walking the tree, stop, hand control back to the browser so it can handle a user event or a paint, and then resume exactly where it left off.


Why Did React Need to Replace the Old Reconciler?

The stack reconciler worked fine for small trees, but it had a hard architectural limit: once it started recursing through your component tree, it couldn’t stop until it finished. On a large tree, that recursive pass could take longer than a single frame budget (roughly 16ms at 60fps), which meant the main thread was blocked and the browser couldn’t respond to input, scroll, or animate anything during that window.

This is the freeze you see in the scenario above. It isn’t that your components are doing unusually heavy computation — it’s that the rendering work, however modest per component, adds up across a large tree and gets executed in one uninterruptible pass.


How Does Fiber Actually Break Work Into Interruptible Units?

Each fiber node corresponds to a component and holds the information React needs to process it: its type, its props, a pointer to its return (parent) fiber, its child, and its sibling. This linked structure lets React walk the tree without relying on the JavaScript call stack, which is what made pausing impossible in the old model.

React processes the tree one fiber at a time. After finishing a unit of work, it checks whether the browser needs the main thread back. If there’s still time left in the current frame, it moves to the next fiber. If not, it yields control, lets the browser do what it needs to do, and picks back up on the next available frame.

FiberNode {
  type: "ComponentType",
  key: null,
  child: FiberNode | null,
  sibling: FiberNode | null,
  return: FiberNode | null, // parent
  pendingProps: {...},
  memoizedProps: {...},
  // plus effect flags, alternate pointer, etc.
}

This is the core mechanism behind everything Fiber enables — it’s a scheduling primitive as much as it is a data structure.


What’s the Difference Between the “Render Phase” and the “Commit Phase”?

Fiber splits rendering into two distinct phases, and this distinction matters for understanding what can and can’t be interrupted.

The render phase is where React figures out what needs to change — running your components, computing new fiber trees, and diffing against the previous tree. This phase is interruptible. React can pause here, abandon the work entirely if a higher-priority update comes in, or throw it away and start over.

The commit phase is where React actually applies those changes to the DOM. This phase is synchronous and cannot be interrupted, because partially applying DOM mutations would leave the page in a visually inconsistent state. It’s typically much shorter than the render phase, which is why yielding during render is where most of the responsiveness gains come from.

Phase Interruptible? What Happens
Render Yes Components run, new fiber tree is built, diffing occurs
Commit No DOM mutations are applied, refs are attached, effects are scheduled

Does Fiber Mean My Components Render “Faster”?

No, and this is a common point of confusion. Fiber doesn’t make any individual component’s render function execute more quickly. If a component does expensive computation on every render, Fiber won’t fix that — you’d still reach for useMemo, React.memo, or restructuring the computation itself.

What Fiber changes is the scheduling of that work relative to everything else the browser needs to do. It lets React prioritize urgent updates (like a keystroke or a click) over less urgent ones (like re-rendering a large list further down the page), and it lets React interleave rendering work with the browser’s own responsibilities so the page doesn’t lock up while React works through a large tree.


How Does This Connect to Concurrent Features Like startTransition?

Fiber is the substrate that makes concurrent rendering possible at all. Without a structure that supports pausing, resuming, and prioritizing units of work, there’d be no way to mark some updates as more urgent than others.

startTransition and useDeferredValue are the developer-facing APIs built on top of Fiber’s scheduling capability. When you wrap a state update in startTransition, you’re telling React that this particular update can be interrupted by something more urgent — a subsequent keystroke, for instance — and that it’s acceptable for the resulting render to complete a little later.

function SearchResults() {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();

  function handleChange(e) {
    const value = e.target.value;
    setQuery(value); // urgent: update the input immediately
    startTransition(() => {
      setResults(computeResults(value)); // can be deferred
    });
  }

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

Here, the input stays responsive because updating it is treated as urgent, while the potentially expensive results computation is scheduled at lower priority and can be preempted if the user keeps typing.


Why Does My Profiler Show Renders Split Across Multiple Frames?

If you’ve profiled a large update and noticed that a single state change produces work spread across several frames instead of one continuous block, that’s Fiber’s yielding behavior visible in the data. It’s a sign the scheduler decided the tree was large enough, or that other work was pending, and chose to break the render phase into smaller chunks rather than blocking the thread for the whole duration.

This is generally a good sign for perceived responsiveness — it means the page can still respond to input mid-render — but it can also make Profiler traces harder to read at first, since what looks like “the same update” in your code shows up as multiple discrete commits in the timeline.


Do I Need to Understand Fiber to Write Fast React Code?

Not in detail, most of the time. You can write performant React applications for years without knowing what a fiber node looks like internally. But understanding the render/commit split and the concept of interruptible, prioritized work explains why certain patterns exist and why certain fixes work.

It’s the difference between memorizing “use startTransition for non-urgent updates” as a rule and understanding why that API exists in the first place — which makes it much easier to recognize the next situation where the same underlying problem shows up in a different shape.


A Quick Reference

Concept What It Means
Fiber node A unit of work representing one component instance
Render phase Interruptible phase where new UI is computed
Commit phase Synchronous phase where DOM changes are applied
Yielding React pausing render work to let the browser handle other tasks
Priority The mechanism that lets urgent updates preempt less urgent ones

Fiber itself won’t fix a slow component, but it’s the reason React can keep a page responsive while that component’s render work is still in progress. If you’re chasing down a freeze like the one in the form example above, the Profiler’s frame-by-frame breakdown is usually the fastest way to see whether the bottleneck is render cost, commit cost, or a missed opportunity to mark an update as deferrable.

If you’re looking at a Profiler trace right now and trying to figure out whether what you’re seeing is a scheduling issue or a raw render-cost issue, describe what the timeline looks like and I can help you narrow it down.