Frontend Development 16 min read

Understanding React Fiber Architecture: From React 15 to React 16

React 16 feels smoother than React 15 because its new Fiber architecture breaks state updates into small, priority‑aware units that the Scheduler runs asynchronously, allowing high‑priority user input to render first while lower‑priority work is paused and resumed, eliminating the lag seen in full‑tree re‑renders of React 15.

HelloTech
HelloTech
HelloTech
Understanding React Fiber Architecture: From React 15 to React 16

This article explains why React 16 feels smoother than React 15 by comparing two examples with a list of 5,000 items whose text changes as the user types. The React 15.7 example shows noticeable lag, while the React 16.8 example runs fluidly.

To understand the improvement, the article first reviews the browser rendering pipeline: browsers refresh at 60 Hz (≈16.6 ms per frame) and have a single main thread that runs JavaScript and performs layout/painting. JavaScript execution and DOM rendering are mutually exclusive, so long‑running JS blocks UI updates.

In React 15, every call to this.setState triggers a full re‑render of the component tree. Updating a list with 10,000 items forces a massive diff operation on the main thread, causing UI jank and delayed user input.

React 16 introduces the Fiber architecture. State updates are split into small units and scheduled asynchronously. High‑priority updates (e.g., user input) are processed first, while lower‑priority work can be paused and resumed, similar to an OS time‑slice scheduler.

The new architecture adds a Scheduler layer on top of the existing Reconciler and Renderer layers:

Scheduler – assigns priorities and decides when a task should run.

Reconciler – finds changed virtual DOM nodes and marks them.

Renderer – applies the marked changes to the real DOM.

React abandoned the native requestIdleCallback API due to compatibility, precision, debugging, and priority‑handling issues, and instead implements its own polyfill within the Scheduler.

Key code snippets illustrate the core ideas:

function workLoopConcurrent() {
  // Perform work until Scheduler asks us to yield
  while (workInProgress !== null && !shouldYield()) {
    workInProgress = performUnitOfWork(workInProgress);
  }
}

Fiber nodes are the fundamental units of work. Each Fiber stores static information (type, tag, elementType, stateNode, parent/child/sibling links) and dynamic information (pendingProps, memoizedProps, updateQueue, lanes, effectTag, etc.).

function FiberNode(tag, pendingProps, key, mode) {
  // static properties
  this.tag = tag;               // FunctionComponent, ClassComponent, HostComponent, ...
  this.key = key;               // React element key
  this.elementType = null;      // DOM tag name or component class
  this.type = null;             // Function or class instance
  this.stateNode = null;        // DOM node or component instance
  this.return = null;           // parent Fiber
  this.child = null;            // first child Fiber
  this.sibling = null;          // next sibling Fiber
  this.index = 0;               // child index
  this.ref = null;              // ref attribute
  // dynamic properties
  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;
  this.mode = mode;
  this.effectTag = NoEffect;
  this.nextEffect = null;
  this.firstEffect = null;
  this.lastEffect = null;
  this.lanes = NoLanes;
  this.childLanes = NoLanes;
  this.alternate = null;       // link to the other version of this Fiber (double buffering)
}

React maintains two Fiber trees simultaneously (double buffering): the current tree that is rendered on screen and a work‑in‑progress tree built in memory. After the work‑in‑progress tree is committed, it becomes the new current tree.

currentFiber.alternate === workInProgressFiber;
workInProgressFiber.alternate === currentFiber;

During the initial mount, ReactDOM.render creates a fiberRoot and a rootFiber . The rootFiber becomes the entry point of the work‑in‑progress tree, which is then reconciled and committed to the DOM.

function App() {
  const [num, addNum] = useState(0);
  return (
number
addNum(num + 1)}>{num}
);
}
ReactDOM.render(
, document.getElementById('root'));

When the user clicks the <span> , a new render phase creates a fresh work‑in‑progress tree, reuses unchanged Fibers from the current tree, and finally swaps the trees so the updated UI appears.

In summary, React 16’s Scheduler and Fiber together provide:

Fine‑grained priority scheduling.

Interruptible, asynchronous rendering.

Improved responsiveness for user interactions.

These enhancements deliver smoother page loads and a more fluid user experience.

PerformancereconciliationFront-endreactSchedulerFiber
HelloTech
Written by

HelloTech

Official Hello technology account, sharing tech insights and developments.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.