Home / Articles / Inside React Fiber: Units of Work, Render vs Commit, and Priority Lanes

This article is published in English.

Inside React Fiber: Units of Work, Render vs Commit, and Priority Lanes

Learn what React Fiber actually is, why the old stack reconciler blocked the main thread, and how units of work, two phases and lanes enable concurrent rendering.

3495 words

"Fiber" is one of those React terms that gets repeated far more often than it gets explained. Developers hear it described as a new engine, a replacement for the Virtual DOM, or something to do with Hooks, and none of those descriptions is quite right. This guide builds the concept from the ground up: first the problem React had before version 16, then what a fiber is, how rendering splits into two phases, and how scheduling and priorities sit on top. By the end you should be able to explain Fiber accurately, spot the common misconceptions, and understand why APIs like startTransition depend on it.

Fiber in one sentence

React Fiber is the internal reconciliation architecture that shipped with React 16. Its job is to make rendering work controllable. Rather than handling an update as a single, indivisible task, React models the work as many small units that it can process one by one and rank by importance.

The shift is easiest to see side by side. Before Fiber, an update followed a straight line from start to finish:

Before Fiber:

Update
  ↓
Render entire tree
  ↓
Commit changes
  ↓
Done

With Fiber, a stage appears in the middle where the work is chopped into pieces, and those pieces can be ordered, paused and picked up again before anything reaches the screen:

After Fiber:

Update
  ↓
Break work into units
  ↓
Process units
  ↓
Prioritize / pause / resume when appropriate
  ↓
Commit changes

That extra stage underpins almost every modern rendering feature in React. To see why it justified a rewrite, look at what came before.

The stack reconciler and its blind spot

React versions before 16 used what is usually called the Stack Reconciler. The overall pipeline was already familiar: a state change leads to a render, the render is reconciled against the previous result, and the DOM is updated.

State Change
    ↓
  Render
    ↓
Reconciliation
    ↓
DOM Updates

The catch was that reconciliation ran synchronously. The name comes from the fact that the reconciler walked the tree through ordinary recursive function calls, so the progress of the work lived on the JavaScript call stack itself. Once React began processing an update, it had no natural way to stop midway, because stopping would mean unwinding that stack and losing its place. Picture a moderately large application:

App
│
├── Header
├── Sidebar
├── Dashboard
│   ├── Chart
│   ├── Table
│   └── Statistics
├── Notifications
└── Footer

If an update touched much of this tree, React would walk through the affected components in one continuous pass, from the first one to the last, without ever handing control back:

Start rendering
      ↓
  Component A
      ↓
  Component B
      ↓
  Component C
      ↓
  Component D
      ↓
  Component E
      ↓
     ...
      ↓
   Finish

The output was fine; the problem was time. React could not interrupt the work and resume it later, so everything else had to wait.

Why a long render hurts the user

JavaScript does not have the main thread to itself. The browser uses the same thread to run event handlers, compute layout, paint pixels and produce frames:

Browser Main Thread
│
├── JavaScript
├── Event Handling
├── Layout
├── Paint
└── Rendering

When a script holds the thread for a long stretch, all of those jobs queue up behind it. From the user's side, the chain of events looks like this:

Click
 ↓
React starts large update
 ↓
Main thread remains busy
 ↓
Browser can't respond quickly
 ↓
UI feels slow

A click registers late, a keystroke appears after a noticeable delay, an animation stutters. In a small app the render pass is short enough that nobody notices. As trees grow and interactions multiply, though, a framework needs a say in when rendering work runs and how much of it runs at once. That is the gap Fiber was designed to close.

The core idea: work as schedulable units

The mental model behind Fiber fits in a single line: rendering is split into manageable units of work that React can schedule and prioritize.

Under the old model, the instruction to the reconciler was effectively a single command:

"Render this entire tree."

Under Fiber, React can instead reason about individual pieces and decide what deserves attention first:

"Here is one piece of work."
"Here is another piece."
"Which work should I process first?"

With discrete pieces instead of a deep recursive call, React can stop after any piece, check for more important work, and continue later.

What a fiber actually is

A fiber is a plain JavaScript object that stands for one unit of work in React's internal tree. In practice there is roughly one fiber per component or element that takes part in reconciliation. Take this small component tree:

App
│
├── Header
├── Sidebar
└── Content

Conceptually, React keeps a parallel structure made of fibers:

Fiber Tree

App Fiber
   │
   ├── Header Fiber
   ├── Sidebar Fiber
   └── Content Fiber

Real fiber objects carry a lot more than a name. They record their links to parent, child and sibling nodes, the type and identity of the component, pending props and state, and the effects that will need to run once the work is committed. Those explicit links are what allow React to walk the tree with a loop instead of recursion, which in turn is what makes stopping and resuming possible. For the architecture as a whole, though, one equation is enough to keep in mind: a fiber is a unit of React's rendering and reconciliation work.

Fiber is not a replacement for the Virtual DOM

A frequent claim is that Fiber "replaced the Virtual DOM". It did not. The two answer different questions.

The Virtual DOM is a description of what the UI should look like, which React compares against the previous description to find out what must change:

Virtual DOM
    ↓
"What should the UI look like?"

Fiber is the machinery React uses to organize and carry out the work needed to reach that target:

Fiber
    ↓
"How should React organize and process the work required to get there?"

They work together, but one is a representation of the UI and the other is a way of processing work. If you want a closer look at the comparison side, see how React's Virtual DOM diffing decides what to update.

The fiber tree during an update

React maintains its fiber tree for the lifetime of the app. A slightly deeper example shows how a subtree nests inside it:

                 App
                  │
        ┌─────────┼─────────┐
        ↓         ↓         ↓
      Header    Sidebar    Content
                            │
                     ┌──────┴──────┐
                     ↓             ↓
                   Chart          Table

When props or state change, React uses this structure to find the branches that need work and skip the rest.

Making rendering interruptible

The single most important consequence of this design is that rendering can be paused. Consider an update that breaks down into several chunks of work:

Large Update
     ↓
   Work 1
     ↓
   Work 2
     ↓
   Work 3
     ↓
   Work 4

With the synchronous reconciler, all four chunks had to run back to back. With Fiber, React can process a couple of them, yield so the browser can respond to input or paint a frame, and then continue:

Work 1
  ↓
Work 2
  ↓
Pause
  ↓
Browser gets an opportunity to handle other work
  ↓
Resume
  ↓
Work 3
  ↓
Work 4

This is not a speed optimization in itself; the total work stays the same. The work simply no longer monopolizes the thread, which gives React room to schedule it.

Two phases: figuring out changes, then applying them

Modern React does not treat an update as a single hop from rendering to the DOM:

Render → DOM

Instead, every update passes through two distinct phases:

             React Update
                  │
                  ↓
             Render Phase
                  │
                  ↓
            Commit Phase
                  │
                  ↓
                 DOM

The render phase computes the next UI

In the render phase, React answers the question of what the UI ought to look like now. Concretely, it:

  • runs component functions and processes pending updates
  • builds or reconciles the fiber tree
  • works out what differs from the current tree
  • collects the list of changes that will need to be applied

In diagram form, the previous tree and the new updates go in, and a description of the required work comes out:

Previous Tree
      +
 New Updates
      ↓
Reconciliation
      ↓
   New Work

Because nothing in this phase touches the DOM, it can be interrupted in modern React. React can compute a large part of the next tree in the background and nobody sees any intermediate state. This also explains why React expects rendering to be free of side effects: under concurrent rendering, a component may be rendered more than once before its result is committed, and in development Strict Mode deliberately double-invokes render logic to surface code that breaks under that assumption.

The commit phase applies the result

Once the changes are known, React commits them:

Render Phase
     ↓
Changes determined
     ↓
Commit Phase
     ↓
DOM updated

The commit phase is where real mutations happen: DOM nodes are inserted, updated or removed, refs are attached, and layout effects run. A handy way to hold the two phases apart:

Render Phase
"Let's figure out what needs to change."

Commit Phase
"Now apply those changes."

Why the commit cannot be paused

If pausing is so useful, why not pause everywhere? Because the screen has to stay consistent. Imagine React stopping halfway through writing to the DOM:

Update A
 ↓
DOM partially changed
 ↓
Pause
 ↓
Update B

The user would see a mix of old and new UI that never logically existed. So React keeps the concerns apart: working out changes may be interrupted, restarted or discarded, but the commit applies a finished result in one go. This boundary is key to reasoning about modern React.

Scheduling: not all updates are equal

With work broken into units, React can start asking which work matters most. Some updates are plainly more urgent than others. Responding to this:

User clicks a button

matters more to the user in that moment than this:

Rendering a large list somewhere else

In the same way, this:

Typing in an input

needs to feel instant. A heavy update happening elsewhere on the page should not make characters lag behind the keyboard. Fiber supplies the structure that lets React reason about such differences and order the work accordingly.

Lanes: how React labels priority

Internally, modern React tags updates with lanes, which encode their priority. Application code almost never deals with lanes directly; React uses them to decide which pending updates to process in a given render and which can wait. A simplified view:

                Updates
                   │
       ┌───────────┼───────────┐
       ↓           ↓           ↓
     Urgent      Normal      Deferred
       │           │           │
       ↓           ↓           ↓
    Process      Process     Process
    sooner       normally    later

It helps to keep three related terms apart when they come up together:

Fiber
 ↓
Represents work

Scheduler
 ↓
Helps coordinate when work should happen

Lanes
 ↓
Represent priority of updates

Fiber describes the work, the scheduler decides when it runs, and lanes express how urgent each update is. The concrete implementation of these pieces has changed between React releases, so treat this as a conceptual model rather than a description of the current source code.

Old versus new, step by step

Before Fiber, reconciliation was synchronous and stack-based, and once it started it simply kept going:

Update
  ↓
Reconcile
  ↓
Continue
  ↓
Continue
  ↓
Continue
  ↓
Finish

There was little room to interrupt the work or to favor one update over another.

The Fiber architecture inserts scheduling decisions into the pipeline:

Update
  ↓
Create / schedule work
  ↓
Process Fiber units
  ↓
Prioritize
  ↓
Pause / resume / restart when appropriate
  ↓
Complete render
  ↓
Commit

Put both flows next to each other and the change becomes obvious:

BEFORE FIBER:

Component Tree
      ↓
Synchronous Reconciliation
      ↓
Finish Everything
      ↓
   Commit


AFTER FIBER:

Component Tree
      ↓
Fiber Tree
      ↓
Units of Work
      ↓
Prioritize / Schedule
      ↓
    Render
      ↓
    Commit

The point was not that React got faster, but that it gained control over how and when rendering work happens.

Common misconceptions

Fiber does not add threads

Fiber does not split React across multiple threads:

React
 ├── Thread 1
 ├── Thread 2
 └── Thread 3

React's JavaScript still runs on the main thread in the browser. Fiber is a form of cooperative scheduling: React voluntarily yields between units of work so that other tasks get a turn. That is fundamentally different from Web Workers, which really do execute code on a separate thread.

Concurrent does not mean simultaneous

Fiber is what makes concurrent rendering possible, but the word "concurrent" is easy to misread. It does not mean React renders everything at the same instant. It means React can prepare an update without blocking the application for the entire duration of one long render. A typical sequence:

Low-priority update
       ↓
React starts rendering
       ↓
Higher-priority update arrives
       ↓
React can prioritize the important work
       ↓
Continue / restart lower-priority work

A low-priority render can be set aside when something more urgent arrives, and then continued or restarted from scratch afterwards. That is the mechanism behind many of the smoother interactions in modern React. For how this plays out in a framework setting, the article on partial pre-rendering and concurrent rendering covers the Next.js angle.

Fiber is not "one component at a time"

It is also tempting to picture Fiber as React rendering exactly one component, then the next, then the next:

Component 1
Component 2
Component 3

That is too simple. React's traversal, batching and scheduling are more involved than a flat list; what Fiber provides is a work model far finer-grained than the old recursive approach.

Transitions in practice

Transitions are where Fiber's scheduling becomes visible in application code. Take a search box where the user has just typed:

User types: "rea"

That keystroke triggers two different kinds of work:

1. Update the input immediately
2. Update a huge search result list

Updating the input is what the user is watching; rebuilding a long result list may be expensive. React lets you mark the second kind as non-urgent by wrapping the state update in startTransition:

startTransition(() => {
    setSearchResults(results);
});

The typed characters are handled as an urgent update so the field stays responsive:

User Input
    ↓
Urgent Update
    ↓
Keep UI responsive

The result list is handled as a transition, which React may render at a lower priority and interrupt if the user keeps typing:

Search Results
    ↓
Transition
    ↓
Can be handled with lower priority

Two practical notes. First, transitions do not make the expensive work cheaper; they only stop it from blocking urgent updates, so a slow list may still benefit from memoization or virtualization. Second, the input's own state should stay outside the transition, otherwise typing itself becomes deferrable and the field feels sluggish. None of this scheduling would be possible without the interruptible render phase that Fiber provides.

Why React needed a new foundation

Before Fiber, React already had most of what people associate with it:

  • a Virtual DOM
  • reconciliation
  • a component model
  • efficient DOM updates

The redesign was about the future, not about fixing something broken. Applications were growing along several axes at once:

Larger
   +
More interactive
   +
More data-driven
   +
More complex

That growth meant React had to care about more than what changed. It also needed to answer questions like when a piece of work should run, how important an update is, whether work can be paused, whether a different update should jump the queue, and how to avoid blocking the interactions users care about most. Fiber is the architecture that makes those questions answerable.

A dashboard scenario

Consider an analytics dashboard with several heavy regions:

Dashboard
│
├── Navigation
├── Filters
├── Revenue Chart
├── User Chart
├── Large Data Table
└── Notifications

When the user changes a filter, a naive implementation might re-render the charts and the big table in one long pass, and the filter control itself would feel slow to respond. Under the Fiber architecture, the same interaction flows through prioritized work:

User changes filter
        ↓
Update begins
        ↓
React performs reconciliation
        ↓
Work is represented through Fiber
        ↓
Updates can be prioritized
        ↓
Rendering completes
        ↓
Commit changes

The charts and table still have to be recalculated. What improves is responsiveness while React manages that work.

Fiber compared with neighboring concepts

Fiber and the Virtual DOM

The Virtual DOM is a representation of the UI that React uses to find out what needs to change:

UI State
   ↓
Virtual Representation

Fiber is the internal architecture and data structure through which React represents and processes rendering work:

Component / Element
       ↓
     Fiber
       ↓
Reconciliation + Scheduling

Together they form React's rendering system:

Virtual DOM
      +
Fiber Architecture
      ↓
React Rendering System

Related, but not interchangeable.

Fiber and Web Workers

These address entirely separate problems. Fiber is concerned with:

Rendering
Reconciliation
Scheduling
Prioritization

A Web Worker, by contrast, is about:

Running JavaScript
outside the main UI execution context

Its structure looks like this, with heavy computation moved off the thread that owns the UI:

Main Thread
     │
     ├── UI
     ├── React
     │
     ↓
Web Worker
     │
     ↓
Heavy computation

Fiber never moves React's rendering into a worker. If you have CPU-heavy work that is not rendering, such as parsing or number crunching, a worker is still the right tool; the article on dedicated, shared and service workers walks through those options.

What this means for your code

In day-to-day work you never touch fibers directly. You write components:

function App() {
    return <Dashboard />;
}

and you trigger updates:

setState(newValue);

React translates those into fiber work behind the scenes. Your code sits at the top of a pipeline whose lower layers you rarely need to think about:

Your React Code
      ↓
React APIs
      ↓
Fiber Architecture
      ↓
Reconciliation
      ↓
Scheduling / Prioritization
      ↓
    Commit
      ↓
     DOM

You never create fiber nodes by hand. What you need is the mental model: keep render logic pure, put side effects in effects or handlers, and use transitions for expensive, non-urgent updates.

The shortest possible summary

The old reconciler followed a simple rule:

"Start rendering → keep going → finish."

Fiber follows a different one:

"Break rendering into work → decide how to schedule it
→ complete the render → commit the result."

That difference is the whole architectural shift in a nutshell.

Wrapping up

The stack reconciler served React well for years. Fiber answered applications that outgrew it. The evolution runs from limited control:

Old React
   ↓
Synchronous Stack Reconciler
   ↓
Limited control over rendering work

to a model where work is explicit, schedulable and prioritized:

React Fiber
   ↓
Fiber Tree + Units of Work
   ↓
More flexible reconciliation
   ↓
Scheduling + Prioritization
   ↓
Concurrent Rendering Capabilities

When someone asks what React Fiber is, "the new rendering engine" undersells it. A more precise answer is that Fiber is React's internal reconciliation architecture, which models rendering as units of work so React can schedule, prioritize, interrupt and resume that work when it makes sense.

Key takeaways

  • Fiber arrived in React 16 and replaced the recursive, synchronous Stack Reconciler.
  • A fiber is an object representing one unit of rendering work, linked into a tree React can traverse and pause.
  • The render phase computes changes and can be interrupted; the commit phase applies them in one uninterrupted step.
  • Lanes and the scheduler use Fiber's structure to give urgent updates priority over deferred ones.
  • Fiber is neither the Virtual DOM nor multithreading; it is cooperative scheduling on the main thread.
  • Concurrent rendering and transitions are built on this foundation, but they manage expensive work rather than eliminating it.