Fiber: the data structure React reconciles over
Reconciliation has to walk a tree, pause partway through, and resume later — so React represents every element as a fiber, a plain JavaScript object with parent/child/sibling pointers that lets the whole tree be traversed without the call stack, one interruptible unit of work at a time.
Fiber: the data structure React reconciles over
The previous lessons described reconciliation as a loop — diff the new tree against the old one, work out the minimal changes, apply them. That description leaves out a question: walk the tree how? A tree walk needs some mechanism to track where you are and what's left to visit. The obvious mechanism is recursion — call yourself on each child, let the JS call stack hold the "come back here after" state. React used exactly that for years. Then it replaced it, because recursion has one fatal property for a UI library: once you start a synchronous recursive walk, you cannot stop in the middle. Fiber is the data structure React built to make stopping in the middle possible.
The old approach: the stack reconciler
React's original reconciler (pre-2017) walked the element tree the way you'd probably write it yourself: a recursive function that processes a node, then recurses into each child, then returns. The JS engine's own call stack tracked the position — which node you're on, which children are left, where to return to. This is simple and it works, with one hard constraint: a synchronous recursive call, once started, runs to completion. There is no built-in way to pause a stack frame halfway through a deep recursive walk and let something else run, then come back.
For a small UI this constraint is invisible. For a large tree — thousands of components, deeply nested — it means one update triggers one long recursive walk that occupies the main thread until it finishes. Nothing else can happen: no click handler runs, no frame paints, no keystroke registers. This is the exact "long task" problem the event loop module spends a whole lesson on, and reconciliation was capable of causing it on every one of React's own updates.
The fix: don't use the call stack — use your own linked structure
If the problem is "the call stack can't be paused," the fix is "stop using the call stack to track tree position." Fiber is React's replacement: instead of implicit position kept in stack frames, each node in the tree is an explicit object — a fiber — that holds its own pointers to the next thing to visit. Walking the tree becomes a plain iterative loop over these objects, and a loop can be paused between iterations, because nothing about "the next iteration hasn't started yet" depends on an unwound call stack. The traversal state lives in the fibers themselves, not in stack frames, so React can stop after any fiber, do something else, and resume later by just looking at where it left off.
What a fiber actually is
A fiber is one plain JavaScript object per element (or component instance) in the tree — the internal unit of work for that element. Conceptually, it looks something like this:
// Illustrative shape — not the literal React source, but the fields that matter.
const fiber = {
type: 'div', // or a component function/class
key: null, // reconciliation identity (the diffing lesson)
pendingProps: {}, // props for the work about to happen
memoizedProps: {}, // props from the last completed render
memoizedState: null, // hooks linked list for this fiber (lesson 6)
child: null, // first child fiber
sibling: null, // next sibling fiber
return: null, // parent fiber (yes, called "return," not "parent")
alternate: null, // the same fiber in the OTHER tree (see below)
flags: 0, // effect tags: what DOM work this fiber needs (lesson 5)
};Two groups of fields matter for this lesson. The first is what to do: type, pendingProps/memoizedProps (props before and after this unit of work runs), and memoizedState (where hooks live, covered in lesson 6). The second is how to get around the tree: child, sibling, and return. Those three pointers are the whole trick. A fiber points at its first child, its next sibling, and its parent — enough to reach every fiber in the tree from any starting point, without a stack, by simple pointer-following.
Notice what's missing: an array of children. A fiber doesn't hold "my children" as a list — it holds one pointer to its first child, and that child holds a pointer to the next sibling. Reaching "all of App's children" means: go to child (Header), then keep following sibling (Main, then Footer) until you hit null. It's a linked list of children threaded through the tree, not an array — a shape chosen specifically because it's cheap to pause and resume: at any fiber, "what's next" is always just one pointer read away.
The work loop: units of work, not one big recursive call
Reconciliation over fibers runs as an explicit loop, not a recursive function call. Each iteration processes one fiber — one "unit of work" — and the loop has two phases per fiber:
beginWork— process the current fiber: figure out what it renders to (calling the component function if it's one), diff its new children against the old ones, and create or reuse child fibers accordingly. Then descend: move tochild.completeWork— once a fiber has no more unprocessed children, finalize its work (this is where the DOM-mutation instructions for that fiber get attached) and move tosiblingif there is one, or up viareturnto the parent and try its sibling, continuing until the walk bubbles back to the root.
This descend-then-bubble pattern visits every fiber exactly once, in the same order a recursive walk would — but as a loop over an explicit "what's the next unit of work" pointer, checked after every single fiber. Between any two units of work, the loop can ask: should I keep going, or should I yield control back to the browser and pick this up again later? That question — and the ability to answer "yield" and mean it — is the entire reason fiber exists. Scheduling and concurrent React is where that yield decision gets made for real, based on priority and how much frame budget is left.
Double buffering: current tree and work-in-progress tree
There's a second problem fiber solves, related but distinct: if building the new tree can be paused, thrown away, or take a while, the screen still has to show something coherent the whole time. React's answer is to keep two fiber trees at once:
- The current tree — the fiber tree matching what's actually committed to the DOM right now. This is what's on screen.
- The work-in-progress (WIP) tree — the tree being built (or rebuilt) by the current render pass, based on the new state.
Every fiber in the current tree has a counterpart fiber in the WIP tree (created on demand, reusing the old object when possible rather than always allocating fresh), and each one points at its counterpart via an alternate field. Rendering never mutates the current tree in place — it builds or updates the WIP tree, fiber by fiber, leaving the current tree fully intact and on-screen the entire time. Only at commit (lesson 5) does React flip which tree counts as "current" — the WIP tree becomes the current tree in one atomic pointer swap, the same trick a double-buffered display uses to avoid ever showing a half-drawn frame.
This is why an in-progress render can be safely abandoned: discarding a WIP tree that never became current has no visible effect, because the current tree — the one the user is looking at — was never touched. Combined with the pausable unit-of-work loop, this is what lets React start a render, stop, throw the WIP tree away entirely if a higher-priority update arrives, and start over — something a stack reconciler mutating one shared tree in place could never do safely.
What this sets up
Fiber is the data structure; render and commit are the two phases that use it. Render and commit phases picks up exactly where this lesson ends — walking through beginWork/completeWork in detail as the interruptible render phase, and then the single uninterruptible commit pass where the WIP tree actually becomes current and the real DOM changes get applied.
Go deeper
- Andrew Clark — React Fiber Architecture — The original design document from the engineer who built fiber, written before release — the most direct primary source on why it's shaped this way.
- React docs — Render and Commit — React's own framing of the phases fiber makes possible, a direct bridge into the next lesson.
- MDN — requestIdleCallback — One of the browser primitives React's scheduler is built on top of to actually perform the yields fiber's structure makes possible.
Check yourself
Answer out loud, as if an interviewer asked. If you hand-wave, reread that section.
- Why couldn't the old stack reconciler be interrupted mid-update, and what concrete problem did that cause on a large tree?
- What is a fiber, physically — what does it hold, and which fields exist specifically to make the tree traversable without the call stack?
- Why does a fiber point to one child and one sibling instead of holding an array of children? What does that shape buy you?
- Describe the beginWork / completeWork loop: which pointer does each phase move along, and in what order does the whole walk visit fibers?
- What is the 'unit of work,' and where in the loop does React get the opportunity to yield control back to the browser?
- What are the current tree and the work-in-progress tree, and what does the alternate pointer connect them for?
- Why is it safe for React to throw away a work-in-progress tree entirely, and what would go wrong if the stack reconciler tried to do the same thing?