Skip to content
DevDepth
← Back to all articles

In-Depth Article

React Time Slicing: How Fiber, Scheduler, Yielding, and Resumption Work

Understand how React time slicing really works: Fiber turns rendering into units of work, the Scheduler decides when to yield, and work-in-progress state lets React resume or restart renders.

Published: Updated: 8 min readreact-internals

React time slicing is often explained as "splitting one long task into many small tasks across frames." That direction is useful, but the more precise version is this:

React does not pause one running JavaScript call stack in the middle. It breaks rendering into resumable Fiber units, periodically yields control back to the browser, and then continues or restarts the render later.

That distinction matters because it explains what React is really doing under the hood:

  • Fiber makes render work pausable
  • Scheduler decides when React should keep working and when it should yield
  • Work-in-progress pointers let React resume the same render later
  • Lanes let higher-priority work interrupt lower-priority work

If you want the bigger picture first, this article pairs well with React concurrent rendering: the underlying principle and how React updates work from setState to a DOM update.

1. What time slicing actually means in React

At a high level, time slicing is React's way of preventing one large render from monopolizing the main thread for too long.

If React rendered a very large tree in one uninterrupted pass, the browser could not respond quickly to:

  • typing
  • clicks
  • scrolling
  • painting

So React takes a different approach:

  1. Break render work into many small units
  2. Process those units in a loop
  3. Repeatedly ask whether it should yield back to the browser
  4. Continue later if more work is still pending

There are two important "what this is not" clarifications:

  • It is not multithreading. React is still running on the browser's single JavaScript thread.
  • It is not literally converting one macro task into microtasks. In the browser, React typically schedules follow-up work as another task, often through MessageChannel.

A better mental model is:

Time slicing is cooperative rendering with resumable units of work.

2. Fiber is the data structure that makes pausing possible

React could not do time slicing with the old stack reconciler because recursive function calls are not resumable in the middle. Once the call stack is running, JavaScript has to keep going until the current chain returns.

Fiber solves that by turning rendering into a manually controlled traversal.

Each Fiber node is a small unit of work, usually representing one component or host node. And each Fiber points to the next places React may need to visit:

  • child: first child
  • sibling: next sibling
  • return: parent

That lets React walk the tree step by step instead of relying on one deep recursive stack.

A simplified mental model looks like this:

current Fiber
  -> child
  -> sibling
  -> return

So instead of "render the whole tree right now," React can do something closer to:

while (workInProgress !== null) {
  performUnitOfWork(workInProgress);
}

The key consequence is that React now has a natural checkpoint after every Fiber unit.

That is the physical basis of time slicing:

Fiber turns one giant render into many small units where React can choose to continue, pause, or restart.

3. Scheduler decides when React should yield

Once rendering is split into small units, React still needs an answer to one question:

How long should React keep working before it gives the browser a chance to do something else?

That is where the Scheduler comes in.

The core idea is cooperative yielding. React keeps working only while it still has enough time budget. When the budget is exhausted, it yields control and schedules more work later.

The browser-side scheduler source contains this logic:

function shouldYieldToHost() {
  const timeElapsed = getCurrentTime() - startTime;
  if (timeElapsed < frameInterval) {
    return false;
  }
  return true;
}

That is the essence of time slicing:

  • record when the slice started
  • keep working for a short budget
  • stop once the budget has been used

The "5ms rule" is a useful shortcut, but not the whole story

Many explanations summarize time slicing as "React works for 5ms, then yields." That is a useful first approximation, and the Scheduler feature flags still default frameYieldMs to 5.

But the current source is a little more nuanced than one universal constant:

  • the browser scheduler budget is still based on short frame-sized yielding
  • some concurrent work loop paths also use a throttled budget, where non-idle work can run longer before yielding

So the safest mental model is not "React always yields after exactly 5ms." It is this:

React uses small time budgets and cooperative checks to avoid blocking the main thread for too long.

Why React uses MessageChannel

In browser environments, React often uses MessageChannel to schedule continuation work.

That choice exists for practical reasons:

  • setTimeout(fn, 0) is affected by timer clamping and is less responsive
  • requestIdleCallback is not consistent enough for React's scheduling needs
  • MessageChannel lets React post follow-up work quickly on a later task

One subtle but important point:

React is not trying to align every chunk exactly with animation frames. The scheduler source explicitly notes that it yields multiple times per frame when needed, rather than treating frame boundaries as the only scheduling boundary.

4. How interruption works: React does not get preempted, it cooperatively exits

React's interruption model is not like a CPU forcibly stopping a running instruction stream.

Instead, the render loop cooperatively checks whether it should yield. A simplified concurrent loop looks like this:

while (workInProgress !== null && !shouldYield()) {
  performUnitOfWork(workInProgress);
}

This is the important part:

  • performUnitOfWork(workInProgress) handles one Fiber
  • after each unit, React checks whether it should continue
  • if the answer becomes "no," the loop exits

That exit is the interruption.

There is no magical snapshotting of the JavaScript call stack. React simply returns from the loop, lets the stack unwind, and hands control back to the browser.

That is why React time slicing is called cooperative:

React stops only at places where React itself decides it is safe to stop.

5. How React resumes: work-in-progress state lives outside one function call

If React exits the loop, how can it continue later from roughly the same place?

Because the render's progress is not stored only on the temporary JavaScript call stack. React keeps key render state in module-level variables inside the reconciler, including pointers such as:

  • the current workInProgress Fiber
  • the current root being rendered
  • the lanes for the render in progress

When the scheduler runs React again, the concurrent render path can continue that existing work instead of rebuilding everything from scratch.

In current source, renderRootConcurrent first checks whether the root or lanes changed. If not, React can continue the in-progress stack. If they did change, React calls prepareFreshStack(...) and starts a new render for the new priority set.

So the real resume story is:

  • React yields
  • the browser gets control
  • a later scheduled callback runs
  • React either continues the same work-in-progress tree or starts a fresh one if priorities changed

That is much closer to what actually happens than saying "React stores the whole execution context like a thread."

6. What happens when a higher-priority update cuts in line

Time slicing would not be that useful if React could only pause and resume the same work forever. The more powerful part is that React can also abandon low-priority work when something more urgent arrives.

For example:

  • a transition is rendering a large list in the background
  • the user clicks or types, producing a more urgent update

Now React has a priority conflict. This is where lanes matter.

React compares the lanes of the work already in progress with the lanes of the newer update. If the new work is more urgent, React can throw away the current in-progress render and prepare a fresh stack for the higher-priority lanes.

That is why transition rendering is interruptible. The official useTransition docs describe transition updates as work that can be interrupted by other state updates.

So the practical rule is:

Time slicing makes rendering pausable. Lanes decide whether React should resume the old work or restart with more urgent work.

7. A concrete mental model for the whole pipeline

If you want to compress everything into one flow, think about React time slicing like this:

  1. Fiber turns the component tree into resumable units of work.
  2. React processes one unit at a time.
  3. Scheduler repeatedly asks whether React should yield.
  4. If React should yield, the loop exits and the browser gets control back.
  5. Later, React continues the same work-in-progress tree or replaces it with a fresh one if higher-priority work arrived.

That is the underlying mechanism behind the common sentence:

"Concurrent rendering can be interrupted."

It is not because React pauses the CPU in the middle of one recursive function. It is because React restructured rendering into a form that can be cooperatively stopped between units of work.

8. What React time slicing is really solving

At the user level, time slicing is not about clever scheduling for its own sake. It exists to protect responsiveness.

Without time slicing, a long render can block:

  • visible updates
  • event handling
  • urgent state changes
  • input responsiveness

With time slicing, React has room to say:

  • "This render is not finished yet"
  • "But I should yield now"
  • "If something more important arrives, I may restart from a better priority"

That is why time slicing belongs inside the bigger concurrent-rendering picture rather than standing alone as a tiny optimization.

9. The summary worth remembering

If you only keep four lines in your head, keep these:

  • Fiber provides the unit of work and the traversal structure
  • Scheduler decides when React should yield
  • work-in-progress pointers let React resume later
  • lanes let urgent work interrupt and replace lower-priority work

Once that clicks, React time slicing stops sounding like a vague performance buzzword. It becomes a concrete cooperation model between Fiber traversal, scheduler budgets, and priority-aware rendering.

Reviewed by

DevDepth Editor

Editor and frontend engineering writer

DevDepth publishes practical guides on React, Next.js, TypeScript, frontend architecture, browser APIs, and performance optimization.

Each article should be reviewed for technical accuracy, code clarity, metadata quality, and internal-link fit before it goes live.

Last editorial review: 2026-03-17

Contact the editor