Dev Encyclopedia
ArticlesTools

Get notified when new content drops

No spam. Just new articles, tools, and updates straight to your inbox.

Dev Encyclopedia

A reference for builders

Content

  • Articles
  • Tools
  • Contact

Connect

  • support@devencyclopedia.com
  • RSS Feed

ยฉ 2026 Dev Encyclopedia

Privacy PolicyTermsDisclaimer
  1. Home
  2. /Blog
  3. /React Fiber Architecture Explained: How React Renders UI
react12 min read

React Fiber Architecture Explained: How React Renders UI

React Fiber is the engine behind every React render. Learn how it works: render phase, commit phase, Lanes, and double buffering, explained with analogies.

By Dev EncyclopediaPublished June 7, 2026
On this page

On this page

  • The Problem Before Fiber
  • What Is React Fiber?
  • The Fiber Tree
  • The Two Phases: Render and Commit
  • How Fiber Prioritizes Work (Lanes)
  • Double Buffering: Two Trees at Once
  • Concurrent Features Fiber Makes Possible
  • What This Means for You as a Developer
  • Frequently Asked Questions

If you have spent any time with React, you have probably bumped into this scenario: you call setState, and somehow React knows to update one part of the screen without touching the rest. You did not tell it which part to update. It figured that out on its own. That is React Fiber doing its job quietly in the background.

Fiber is not a feature you call or a package you install. It is the core engine inside React, the thing that decides what to render, when to render it, and how to get your changes onto the screen without freezing the page. Understanding it will change how you think about performance, hooks, and a handful of React behaviors that otherwise look like arbitrary rules.

This guide walks through Fiber the way you would explain it to a developer who already builds with React but has never looked under the hood: in plain English, with analogies, and with a clear line from each concept to the code you write every day. If you have ever wondered why ARIA attributes or layout updates need to land at exactly the right moment, the timing rules in how React's rendering model affects accessibility trace back to the same render and commit phases covered here.

Diagram showing a browser window split between raw HTML and DOM on one side and React components as colored boxes on the other, with React Fiber labeled as the engine orchestrating updates between them
Fiber sits between your component tree and the DOM, deciding what changes and when.

๐Ÿ’ก TL;DR

Fiber is React's rendering engine since version 16. It breaks rendering into small interruptible units (fibers), splits work into a render phase (can pause, can be thrown away) and a commit phase (atomic, visible), assigns every update a priority lane, and keeps two copies of the UI tree so you never see a half-finished screen. This is what makes typing stay smooth even while a big list re-renders in the background.

The Problem Before Fiber

Before React 16, React used something called the Stack Reconciler. When a state update happened, React would walk the entire component tree from the top, compute every change, and write it all to the DOM in one go, with no ability to stop or pause partway through.

Picture a chef in the middle of plating a ten-course meal who cannot stop for anything. A waiter runs in saying "Table 3 needs water urgently," and the chef cannot help until every course is plated. The waiter just stands there. Table 3 gets more frustrated by the second.

That is exactly what happened in a React app built on the Stack Reconciler. A slow component rendering a huge list would block the browser's main thread completely. Typing, clicking, and scrolling all had to wait in line behind it. The result was the kind of janky, unresponsive UI that makes users think your app is broken.

React needed a way to pause mid-render, jump to something more urgent like a keystroke, and come back later to finish the rest. That requirement is the entire reason Fiber exists.

Timeline diagram showing the old Stack Reconciler rendering an entire component tree as one long uninterruptible red bar that blocks the browser's main thread, while a small blue bar labeled user input waits stuck behind it
Under the old Stack Reconciler, one slow render blocked everything else, including user input.

What Is React Fiber?

React Fiber is a complete rewrite of React's rendering engine, introduced in React 16. Instead of one uninterruptible pass over the whole tree, Fiber breaks all rendering work into small units of work called, fittingly, fibers.

A fiber is just a JavaScript object that represents one component instance. It stores everything React needs to know about that component: its type, its props, its current state, what work still needs to happen on it, and pointers to its parent, its first child, and its next sibling.

Think of the old system as a single tall stack of plates. Once you start washing from the top, you cannot stop until the stack is empty. Fiber is a to-do list instead: each card is one task, you can put the list down and pick it back up at any time, and you can reorder the cards by priority whenever something more urgent shows up.

Two-panel illustration comparing the old Stack Reconciler, shown as a tall stack of plates that must be washed top to bottom without stopping, against React Fiber, shown as a to-do list of individual task cards that can be picked up, set down, and reordered by priority
Stack Reconciler: an unstoppable stack. Fiber: a reorderable to-do list of work units.

The Fiber Tree

React builds a tree of fiber nodes, one fiber per component, that mirrors your component hierarchy. At the root sits the App component, branching down through layouts, pages, and individual elements, the same shape you would draw if you sketched your component tree on a whiteboard.

What makes this tree clever is that it is not a typical parent-with-an-array-of-children structure. Each fiber node carries exactly three pointers:

  • child โ€” points to the fiber's first child component
  • sibling โ€” points to the next fiber at the same level in the tree
  • return โ€” points back up to the parent fiber
Tree diagram of fiber nodes with App at the root, branching to Header and Main, and Main branching to Sidebar and Content, with labeled arrows on each node showing a green child pointer down, a blue sibling pointer sideways, and an orange return pointer up to the parent
Each fiber node carries child, sibling, and return pointers, turning the tree into a walkable linked list.

This singly-linked-list shape is what lets React walk the tree incrementally. It can process one node, stop, go do something else entirely, and later resume from that exact node using these pointers, something an ordinary recursive function call cannot do once it is mid-stack. Recursion would have to run to completion or unwind; a fiber can simply be set aside and picked back up.

It is a similar idea to how the `:has()` selector lets CSS reach upward through a tree that was traditionally only walkable downward: giving a structure extra pointers unlocks traversal patterns the original shape never allowed.

The Two Phases: Render and Commit

Every React update runs through two distinct phases. This split is one of the most useful mental models you can carry around as a React developer, because it explains a surprising amount of hook behavior.

  1. Render phase โ€” React walks the fiber tree, runs your component functions, and figures out what changed. Nothing touches the DOM yet, so this phase is invisible to the user. Critically, it can be paused, resumed, restarted from scratch, or thrown away entirely if a more urgent update comes in, like a keystroke.
  2. Commit phase โ€” React takes everything it figured out during the render phase and writes it to the DOM: inserting, updating, and removing elements. useLayoutEffect callbacks fire here too. This phase is synchronous and atomic. It cannot be paused, because the user must never see a half-applied update.

A useful analogy: the render phase is like editing a document in draft mode. You can revise, undo, and rewrite freely, and nobody sees any of it. The commit phase is hitting publish. Once it goes out, it is live, and there is no taking it back mid-stream.

Render phaseCommit phase
What happensRuns component functions, diffs old vs. new, builds the next treeApplies DOM changes, runs layout effects
Can be paused?Yes, fully interruptibleNo, synchronous and atomic
Visible to the user?No, no DOM writes occurYes, this is what the user sees
Can run more than once?Yes, may restart for a higher-priority updateRuns exactly once per committed update
Horizontal pipeline diagram split into two halves: a light blue Render Phase half listing run component functions, diff old versus new, and build work in progress tree, marked with a pause icon meaning it can be interrupted, and a darker green Commit Phase half listing apply DOM changes, run useLayoutEffect, and run useEffect, marked with a lock icon meaning it cannot be paused
The render phase can pause and restart. The commit phase runs once, atomically, and is what the user actually sees.

โ„น Why this matters in practice

If your component does something with side effects directly inside its function body (logging, mutating a module-level variable, kicking off a network request outside an effect), the render phase's freedom to restart and re-run means that code can run more times than you expect. That is also exactly what React's Strict Mode is designed to surface.

How Fiber Prioritizes Work (Lanes)

Not every update is equally urgent. If your app is loading data in the background while the user clicks a button, React should handle that click immediately rather than make the user wait for the data fetch to settle first.

React solves this with a system called Lanes. Every update gets assigned a lane, essentially a priority level, represented internally as bits in a 32-bit integer so React can compare and combine priorities cheaply. From highest to lowest priority, the lanes you will encounter most often are:

  • SyncLane โ€” the highest priority. Direct user interactions like onClick and onChange. Cannot be interrupted.
  • InputContinuousLane โ€” continuous input events such as scrolling and dragging.
  • DefaultLane โ€” ordinary state updates that did not request any special priority.
  • TransitionLane โ€” updates explicitly wrapped in useTransition, marked as non-urgent by you, the developer.
  • IdleLane โ€” the lowest priority. Only runs once nothing else is pending.
Illustration of a multi-lane highway where the fastest lane is labeled SyncLane for user clicks and keystrokes and shows a sports car, the middle lanes are labeled DefaultLane for regular state updates with a car and TransitionLane for non-urgent updates with a slow truck, and the rightmost lane is labeled IdleLane for background work with a bicycle, with a caption noting React always clears the fast lanes first
React always clears the high-priority lanes, like clicks and keystrokes, before working through slower ones.

Picture a highway with a fast lane reserved for emergency vehicles, regular lanes for everyday traffic, and a slow shoulder for things that are in no hurry. React always clears the fast lane first. When a click fires, React tags that update as SyncLane, immediately drops whatever low-priority work it was doing, handles the click, and only then returns to the deferred work. That is the actual mechanism behind why typing in a search box stays smooth even while a large list filters in the background.

You get to use this system directly through useTransition, which tells React "this update can wait if something more important shows up":

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

function handleSearch(query: string) {
  setSearchInput(query);          // urgent: keeps the input feeling instant

  startTransition(() => {
    setFilteredResults(filterHugeList(query)); // non-urgent: gets TransitionLane
  });
}

Without startTransition, both updates land in the same default lane and compete for the same slice of work. With it, React keeps the input itself snappy and lets the expensive list filtering yield whenever something more urgent, like the next keystroke, arrives.

Double Buffering: Two Trees at Once

Here is a trick React borrows from game engines: it keeps two copies of the fiber tree in memory at all times.

  • The current tree โ€” what is actually on screen right now.
  • The work-in-progress tree โ€” where React is assembling the next version of the UI, off-screen and invisible.

When a state update fires, React does not touch the current tree at all. It builds a fresh work-in-progress tree alongside it. Once the render phase finishes and the commit phase begins, React swaps the two: the work-in-progress tree becomes the new current tree, and the old current tree is recycled, often reused as the base for the next update.

This is precisely why you never see a partially updated screen in React. There is always one complete, consistent tree being displayed, and the next one is built entirely in the background before it ever becomes visible.

Side-by-side diagram showing the current tree on the left, displayed live with green checkmarked component boxes, and the work in progress tree on the right, being built off-screen with yellow boxes and pencil icons, connected by a large double-headed arrow in the center labeled atomic swap at commit
React builds the next UI in a hidden work-in-progress tree, then swaps it in atomically once it's ready.

Concurrent Features Fiber Makes Possible

Fiber was not built purely as an internal optimization. It was designed from day one to enable an entire category of features that simply could not exist on top of the old Stack Reconciler. Here are the ones you can reach for directly in your own code today.

  • Time slicing โ€” React splits a long render across multiple browser frames so the page stays responsive. Active automatically in React 18 and later; you do not need to opt into it.
  • Suspense โ€” React can pause rendering part of the tree while it waits on data, show a fallback in the meantime, and resume once the data arrives. None of this is possible without an interruptible, resumable render phase, which is exactly what Fiber provides.
  • `useTransition` โ€” lets you explicitly mark a state update as low priority, so React keeps showing the previous UI until the new one is ready instead of blocking on it.
  • Automatic batching โ€” in React 18, multiple setState calls inside the same event handler, including inside async functions and promises, get grouped into a single render pass. Fiber's scheduler is what coordinates that grouping under the hood.
Split-screen comparison: the left side labeled without useTransition shows a loading spinner blocking the entire page while a list filters, and the right side labeled with useTransition shows the old list staying visible and interactive while the filtered results compute in the background with a small loading indicator in the corner
Wrapping an expensive update in useTransition keeps the previous UI visible and interactive instead of freezing it.

๐Ÿ’ก Tip

If you are reaching for useTransition for the first time, start with the one place users notice lag the most: filtering or sorting a large list as they type. Wrap the expensive update in startTransition and leave the input's own state update outside it. That single change usually produces the most visible improvement for the least code.

What This Means for You as a Developer

Once you have the render-phase-versus-commit-phase model in your head, several React behaviors stop looking like arbitrary rules and start looking like direct consequences of how Fiber works.

  • Why `key` props matter so much โ€” Fiber uses key to match fibers across renders. Change the key and React treats the element as an entirely new component instance, discarding the old fiber and its state. If you have ever fixed a stale-state bug by adding a key, this is the mechanism you triggered.
  • Why `useEffect` runs after paint โ€” useEffect callbacks are scheduled to run after the commit phase completes and the browser has painted. That delay is intentional: effects should never block the user from seeing the updated screen. It is also why timing bugs in effects feel a lot like the async/await ordering mistakes that trip up plain JavaScript code: in both cases, code you assumed ran immediately actually runs on a later turn of the event loop.
  • Why `useLayoutEffect` runs before paint โ€” useLayoutEffect runs synchronously during the commit phase, before the browser paints anything. Reach for it specifically when you need to measure or adjust the DOM (reading an element's size, repositioning a tooltip) before the user ever sees the new layout.
  • Why Strict Mode renders components twice in development โ€” React deliberately double-invokes component functions (and some effects) in Strict Mode to catch code that produces side effects during the render phase. Because the render phase can legitimately restart at any time, your components must produce the same output for the same input every time. Strict Mode exists to surface the cases where they don't.

Frequently Asked Questions

Is React Fiber something I need to install separately?

No. Fiber has been React's internal rendering engine since React 16, and you have been using it the entire time you have written React code on a modern version. It is not a library, a package, or an opt-in feature. It is the core of React itself, doing its job invisibly every time a component renders.

What is the difference between the render phase and the commit phase?
Render phaseCommit phase
What happensRuns component functions, diffs old vs. new, builds the work-in-progress treeApplies DOM changes and runs layout effects
Can be paused?Yes, fully interruptibleNo, synchronous and atomic
Visible to the user?No, no DOM writes happen hereYes, this is the moment the screen actually changes

The short version: the render phase is React thinking, and the commit phase is React acting. Thinking can be interrupted and redone; acting happens once, atomically, so the user only ever sees finished results.

How does React decide which updates are urgent?

React assigns every update a lane, a priority level represented as bits in a 32-bit integer so multiple priorities can be compared and combined cheaply. Direct interactions like clicks and key presses get SyncLane, the highest priority, while updates wrapped in useTransition get TransitionLane, a much lower one.

When a high-priority update comes in, React pauses or discards lower-priority work in progress, handles the urgent update first, and resumes the deferred work afterward. That is the entire reason a UI built on Fiber stays responsive even while heavy rendering happens in the background.

What is the work-in-progress tree?

It is the second copy of the fiber tree React keeps in memory whenever an update is in flight. Rather than editing the tree currently on screen, React assembles the entire next version of the UI in this off-screen tree, computes every change there, and only swaps it in for the visible tree once the commit phase begins.

The practical payoff is that the user is never shown a half-updated UI. They either see the old, complete tree or the new, complete tree, never something in between.

Does understanding Fiber actually help me write better React code?

Yes, directly. It is the model that explains why key props affect component identity and state, why useEffect and useLayoutEffect fire at different moments, why stable references and pure components reduce unnecessary work, and when reaching for useTransition actually pays off versus letting React batch updates on its own.

Once "the render phase is invisible and restartable, the commit phase is visible and final" clicks for you, it stops being trivia and starts being the lens you reach for every time you debug a timing issue or reach for a new hook.

Related Articles

react

ARIA in React: Stop Using aria-label Wrong

Pages using ARIA average 41% more accessibility errors. Learn the correct ARIA patterns for React: icon buttons, modals, toasts, spinners, and tab panels.

Jun 1, 2026ยท10 min read
html css

8 CSS :has() Patterns You'll Actually Use (2026)

CSS :has() is production-ready in every browser. Here are 8 real-world patterns: form states, sibling dimming, modal scroll-lock, and more.

May 30, 2026ยท9 min read
javascript

5 async/await Mistakes That Slow Your JavaScript Code

Sequential awaits, await in forEach, missing Promise.all: these 5 async/await mistakes silently slow your JavaScript. Here's how to spot and fix each one.

May 30, 2026ยท8 min read

On this page

  • The Problem Before Fiber
  • What Is React Fiber?
  • The Fiber Tree
  • The Two Phases: Render and Commit
  • How Fiber Prioritizes Work (Lanes)
  • Double Buffering: Two Trees at Once
  • Concurrent Features Fiber Makes Possible
  • What This Means for You as a Developer
  • Frequently Asked Questions