Understanding React Fiber: How It Boosts Rendering Performance

This article explains why React introduced the Fiber architecture, how it breaks rendering work into small interruptible units, the role of requestAnimationFrame and requestIdleCallback, the underlying linked‑list data structures, and provides code examples that illustrate the render and commit phases for smoother UI updates.

Alibaba Terminal Technology
Alibaba Terminal Technology
Alibaba Terminal Technology
Understanding React Fiber: How It Boosts Rendering Performance

Fiber Design Philosophy

Fiber is a rewrite of React's core algorithm introduced in React 16 to split rendering work into small units, allowing the browser to interleave rendering with user interactions and avoid long‑blocking tasks.

Why Fiber is Needed

Browsers render frames at ~60 fps; if a task exceeds 16 ms the UI becomes janky. The article lists the seven steps performed in each frame (input, timers, begin frame, requestAnimationFrame, layout, paint, idle) and explains how long‑running tasks block the main thread.

From Reconciliation to Fiber

Before Fiber, React performed a recursive diff of the virtual DOM and updated synchronously, which could monopolize the main thread. Fiber breaks the work into interruptible units, uses requestIdleCallback and requestAnimationFrame to schedule work, and can pause and resume rendering.

Comparison with Vue

Vue uses a template‑watcher model that already splits updates into very small tasks, so it does not need a Fiber‑like scheduler.

Core APIs

requestAnimationFrame

schedules a callback before the next paint; an example animates a <div> width from 0 to 100 px and logs frame intervals.

let btn = document.getElementById('start');
let div = document.getElementById('div');
let start = 0;
let allInterval = [];
const progress = () => {
  div.style.width = div.offsetWidth + 1 + 'px';
  div.innerHTML = div.offsetWidth + '%';
  if (div.offsetWidth < 100) {
    let current = Date.now();
    allInterval.push(current - start);
    start = current;
    requestAnimationFrame(progress);
  } else {
    console.log(allInterval);
  }
};
btn.addEventListener('click', () => {
  div.style.width = 0;
  let current = Date.now();
  start = current;
  requestAnimationFrame(progress);
});
requestIdleCallback

runs low‑priority work when the browser has idle time; the article shows a task queue that executes tasks while time remains, and a version where each task sleeps >16 ms, forcing multi‑frame execution.

let taskQueue = [
  () => { console.log('task1 start'); console.log('task1 end'); },
  () => { console.log('task2 start'); console.log('task2 end'); },
  () => { console.log('task3 start'); console.log('task3 end'); }
];
const performUnitWork = () => taskQueue.shift();
const workloop = deadline => {
  while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && taskQueue.length) {
    performUnitWork();
  }
  if (taskQueue.length) {
    requestIdleCallback(workloop, { timeout: 1000 });
  }
};
requestIdleCallback(workloop, { timeout: 1000 });

Fiber Data Structure

Fiber nodes form a singly‑linked list tree with properties such as type, key, stateNode, child, sibling, return, memoizedState, pendingProps, etc. The article provides a minimal Update and UpdateQueue implementation that stores payload and a pointer to the next update.

class Update {
  constructor(payload, nextUpdate) {
    this.payload = payload;
    this.nextUpdate = nextUpdate;
  }
}
class UpdateQueue {
  constructor() {
    this.baseState = null;
    this.firstUpdate = null;
    this.lastUpdate = null;
  }
  enqueueUpdate(update) {
    if (!this.firstUpdate) {
      this.firstUpdate = this.lastUpdate = update;
    } else {
      this.lastUpdate.nextUpdate = update;
      this.lastUpdate = update;
    }
  }
  forceUpdate() {
    let state = this.baseState || {};
    let cur = this.firstUpdate;
    while (cur) {
      const next = typeof cur.payload === 'function' ? cur.payload(state) : cur.payload;
      state = { ...state, ...next };
      cur = cur.nextUpdate;
    }
    this.firstUpdate = this.lastUpdate = null;
    this.baseState = state;
    return state;
  }
}
let queue = new UpdateQueue();
queue.enqueueUpdate(new Update({ name: 'www' }));
queue.enqueueUpdate(new Update({ age: 10 }));
queue.enqueueUpdate(new Update(state => ({ age: state.age + 1 })));
queue.enqueueUpdate(new Update(state => ({ age: state.age + 1 })));
queue.forceUpdate();
console.log(queue.baseState); // { name: 'www', age: 12 }

Traversal and Effect List

The render phase walks the Fiber tree (post‑order) using beginWork and completeUnitOfWork, collects side‑effects into an effect list, and yields to the browser when the frame time runs out.

const beginWork = fiber => console.log(`${fiber.key} start`);
const completeUnitOfWork = fiber => console.log(`${fiber.key} end`);
const performUnitOfWork = fiber => {
  beginWork(fiber);
  if (fiber.child) return fiber.child;
  while (fiber) {
    completeUnitOfWork(fiber);
    if (fiber.sibling) return fiber.sibling;
    fiber = fiber.return;
  }
};
let nextUnit = rootFiber;
while (nextUnit) nextUnit = performUnitOfWork(nextUnit);
console.log('reconciliation phase finished');

During completeUnitOfWork each fiber merges its own effects into its parent’s firstEffect / lastEffect linked list, building the final effect list for the root.

Commit Phase

The commit phase iterates the effect list and applies INSERT, DELETE, or UPDATE operations to the DOM. This phase is non‑interruptible.

const commitWork = fiber => {
  if (!fiber) return;
  const parentDom = fiber.return.stateNode;
  if (fiber.effectTag === 'INSERT') {
    parentDom.appendChild(fiber.stateNode);
  } else if (fiber.effectTag === 'DELETE') {
    parentDom.removeChild(fiber.stateNode);
  } else if (fiber.effectTag === 'UPDATE') {
    if (fiber.type === 'TEXT' && fiber.alternate.props.text !== fiber.props.text) {
      fiber.stateNode.textContent = fiber.props.text;
    }
  }
  fiber.effectTag = null;
};
const commitRoot = () => {
  let fiber = workInProgressRoot.firstEffect;
  while (fiber) {
    commitWork(fiber);
    fiber = fiber.nextEffect;
  }
  currentRoot = workInProgressRoot;
  workInProgressRoot = null;
};

Putting It All Together

A simplified work loop uses requestIdleCallback to repeatedly call performUnitOfWork until all work is done, then calls commitRoot to flush changes.

const workLoop = deadline => {
  let shouldYield = false;
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    shouldYield = deadline.timeRemaining() < 1;
  }
  if (!nextUnitOfWork && workInProgressRoot) {
    console.log('render phase finished');
    commitRoot();
  }
  requestIdleCallback(workLoop, { timeout: 1000 });
};
requestIdleCallback(workLoop, { timeout: 1000 });

The article concludes that Fiber enables smoother user experiences by preventing long‑running tasks from blocking rendering, and encourages readers to explore the React source for deeper details such as task priority and interruption handling.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performanceRenderingJavaScriptReactFiber
Alibaba Terminal Technology
Written by

Alibaba Terminal Technology

Official public account of Alibaba Terminal

0 followers
Reader feedback

How this landed with the community

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.