This page describes the lane model that React's reconciler uses to prioritize, schedule, and track concurrent work. It covers the lane bit-field representation, how lanes are assigned to updates, how the root tracks pending work, how the work loop selects which lanes to process, entanglement, expiration, and the bridge between lanes and the underlying scheduler.
For the broader work loop that consumes lanes, see Work Loop and Rendering Phases. For how Suspense interacts with retry lanes, see Suspense and Error Boundaries. For the underlying scheduler package that lanes map to, see React Reconciler Overview.
A lane is a 32-bit integer where each bit position represents a distinct priority channel. The Lanes type is a bitmask of one or more lanes. This design lets the reconciler:
includesSomeLane, isSubsetOfLanes).mergeLanes, removeLanes).There are 31 usable lane bits (the 32nd is unused). The constant TotalLanes = 31 is exported from ReactFiberLane.js and consumed by ReactFiberReconciler.js packages/react-reconciler/src/ReactFiberReconciler.js127
The central file is:
packages/react-reconciler/src/ReactFiberLane.js — defines all lane constants, lane-map types, and the operations on them.Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js179-221 packages/react-reconciler/src/ReactFiberReconciler.js95-101 packages/react-reconciler/src/ReactFiberBeginWork.js153-166
Lanes are ordered from highest priority (lowest bit) to lowest priority (highest bit). The table below lists the named lane constants exported by ReactFiberLane.js and the performance track group each belongs to (as determined by getGroupNameOfHighestPriorityLane, which is used in ReactFiberPerformanceTrack.js).
| Lane Constant | Priority Group | Typical Trigger |
|---|---|---|
SyncLane | Blocking | flushSync, legacy roots, error recovery |
InputContinuousHydrationLane | Blocking | Hydration at continuous input priority |
InputContinuousLane | Blocking | Continuous input events (pointer drag, etc.) |
DefaultHydrationLane | Blocking | Hydration at default priority |
DefaultLane | Blocking | setState, root.render(), discrete events |
SyncUpdateLanes | Blocking | Combined sync-equivalent lanes |
GestureLane | Gesture | Gesture-driven view transitions |
TransitionLane1–TransitionLane16 | Transition | startTransition updates |
RetryLane1–RetryLane4 | Suspense | Suspense retry after promise resolves |
SelectiveHydrationLane | — | User-event-triggered selective hydration |
IdleHydrationLane | Idle | Background SSR hydration |
IdleLane | Idle | Idle-priority background work |
OffscreenLane | — | Deferred / hidden-subtree pre-rendering |
Convenience sentinel values:
| Constant | Meaning |
|---|---|
NoLane | Zero — no lane |
NoLanes | Zero — empty set |
SomeRetryLane | A non-specific retry lane used for comparisons |
SomeTransitionLane | A non-specific transition lane for comparisons |
UpdateLanes | Union of all non-idle, non-offscreen update lanes |
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js179-221 packages/react-reconciler/src/ReactFiberPerformanceTrack.js22-110 packages/react-reconciler/src/ReactProfilerTimer.js18-25
Lane Hierarchy and Code Entities
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js222-228 packages/react-reconciler/src/ReactProfilerTimer.js18-25
Every FiberRoot object carries a set of lane bitmasks that record the state of pending work. These fields are read by getNextLanes to decide what to render next.
| Field | Type | Role |
|---|---|---|
pendingLanes | Lanes | All lanes with unconsumed work |
suspendedLanes | Lanes | Lanes that are suspended (awaiting a ping) |
pingedLanes | Lanes | Suspended lanes that have been pinged (resolved) |
expiredLanes | Lanes | Lanes whose deadline has passed; must run synchronously |
finishedLanes | Lanes | Lanes included in the most recent completed render |
errorRecoveryDisabledLanes | Lanes | Lanes where error recovery has been suppressed |
entangledLanes | Lanes | Lanes that have entanglement dependencies |
entanglements | LaneMap<Lanes> | Per-lane sets of entangled lanes that must commit together |
expirationTimes | LaneMap<number> | Wall-clock deadline for each lane |
hiddenUpdates | LaneMap<Array<...> | null> | Updates deferred by useDeferredValue inside hidden trees |
The functions that mutate these fields are all defined in ReactFiberLane.js and imported into ReactFiberWorkLoop.js:
markRootUpdated(root, lane) — sets bits in pendingLanes, records event time.markRootSuspended(root, lanes) — moves lanes from pending to suspended.markRootPinged(root, pingedLanes) — marks pingedLanes bits so they can retry.markRootFinished(root, finishedLanes, remainingLanes) — clears the finished lanes and updates expirationTimes.Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js200-207
FiberRoot Lane State Machine
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js200-207 packages/react-reconciler/src/ReactFiberWorkLoop.js196-199
requestUpdateLanerequestUpdateLane(fiber) is called at the top of every state update path — setState, dispatchAction, updateContainer, etc. — to determine which lane the new update belongs to.
Defined at packages/react-reconciler/src/ReactFiberWorkLoop.js804-848
Decision logic:
ConcurrentMode not set on the fiber) → always returns SyncLane.executionContext & RenderContext is set) → returns pickArbitraryLane(workInProgressRootRenderLanes) — the update inherits the current render's lane.startTransition (requestCurrentTransition() returns non-null) → calls requestTransitionLane(transition) from ReactFiberRootScheduler.js, which claims the next available transition lane.eventPriorityToLane(resolveUpdatePriority()). resolveUpdatePriority() is provided by the host config (ReactFiberConfig) and returns the current event's priority. eventPriorityToLane maps that to a specific lane in ReactEventPriorities.js.Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js804-848
| Function | Location | Use Case |
|---|---|---|
requestUpdateLane | ReactFiberWorkLoop.js:804 | All state updates |
requestRetryLane | ReactFiberWorkLoop.js:850 | Suspense retries via claimNextRetryLane() |
requestDeferredLane | ReactFiberWorkLoop.js:864 | useDeferredValue — claims a transition lane or reuses OffscreenLane during prerendering |
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js850-901
ensureRootIsScheduledAfter an update is enqueued and markRootUpdated records the lane on the root, scheduleUpdateOnFiber calls ensureRootIsScheduled(root) (from ReactFiberRootScheduler.js) packages/react-reconciler/src/ReactFiberWorkLoop.js403-406
ensureRootIsScheduled calls getNextLanes(root, NoLanes) to determine the next priority to schedule, then:
SyncLane or expired lanes, schedules a synchronous microtask flush.Scheduler_scheduleCallback.The mapping from lanes to Scheduler priorities goes through lanesToEventPriority in ReactEventPriorities.js, which returns DiscreteEventPriority, DefaultEventPriority, or IdlePriority — then the root scheduler converts that to ImmediateSchedulerPriority, NormalSchedulerPriority, or IdleSchedulerPriority for the scheduler package.
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js69-77 packages/react-reconciler/src/ReactFiberWorkLoop.js403-406
getNextLanesgetNextLanes(root, wipLanes) is defined in ReactFiberLane.js and called at the start of every render attempt. It returns the set of lanes that should be included in the next render. Its logic:
pendingLanes === NoLanes, return NoLanes (nothing to do).expiredLanes) are always included and force the render to be synchronous.suspendedLanes.entangledLanes, all lanes in root.entanglements[laneIndex] are merged in.wipLanes (lanes already being rendered) to handle interleaved updates.The result is stored in workInProgressRootRenderLanes at the start of a render packages/react-reconciler/src/ReactFiberWorkLoop.js440
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js196-221 packages/react-reconciler/src/ReactFiberWorkLoop.js440
Several module-level variables in ReactFiberWorkLoop.js track lane state throughout a single render:
| Variable | Type | Purpose |
|---|---|---|
workInProgressRootRenderLanes | Lanes | The lanes being rendered in this pass |
entangledRenderLanes | Lanes | Superset of render lanes, extended when entering hidden subtrees |
workInProgressRootSkippedLanes | Lanes | Lanes on fibers seen but not included (lower priority updates) |
workInProgressRootInterleavedUpdatedLanes | Lanes | Lanes updated concurrently during this render |
workInProgressRootRenderPhaseUpdatedLanes | Lanes | Lanes updated in the render phase itself |
workInProgressRootPingedLanes | Lanes | Lanes pinged during this render |
workInProgressDeferredLane | Lane | The lane claimed by useDeferredValue in this render |
workInProgressSuspendedRetryLanes | Lanes | Retry lanes spawned by this render that are still suspended |
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js440-500
renderLanesEvery fiber is processed with renderLanes threaded through beginWork packages/react-reconciler/src/ReactFiberBeginWork.js340-371 and completeWork. During beginWork, checkScheduledUpdateOrContext(current, renderLanes) determines whether a fiber has pending updates in the current render. If the fiber's lanes field is not a subset of renderLanes, the update is skipped and its lane is accumulated into workInProgressRootSkippedLanes via markSkippedUpdateLanes.
In processUpdateQueue (called during beginWork for class components and the root), each update node's lane is tested with isSubsetOfLanes(renderLanes, update.lane). Updates whose lane is not included are left on the queue and their lane is retained in workInProgress.lanes.
Sources: packages/react-reconciler/src/ReactFiberBeginWork.js153-166 packages/react-reconciler/src/ReactFiberWorkLoop.js483-500
Entanglement couples lanes so they must commit together. This is used by startTransition to ensure a transition's optimistic state and its data-loading update are committed atomically.
entangleTransitions(root, fiber, lane) (from ReactFiberClassUpdateQueue.js) sets bits in root.entangledLanes and updates root.entanglements[laneIndex].getEntangledLanes(root, renderLanes) is called by getNextLanes to expand the selected lane set to include all entangled lanes.When DiscreteEventPriority updates occur inside a transition, they are also entangled back into the transition's lane to maintain consistency.
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js196-221
Lane expiration prevents starvation: if a lane's update has been pending too long, it is added to expiredLanes, forcing getNextLanes to include it and performConcurrentWorkOnRoot to run synchronously (no yielding).
markRootFinished resets the expiration time for completed lanes.expiredLanes is checked by includesExpiredLane(root.expiredLanes, lanes) before entering the render loop.upgradePendingLanesToSync(root, lanes) is used in error recovery to force a retry at sync priority packages/react-reconciler/src/ReactFiberWorkLoop.js198Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js193-199
Retry lanes are used exclusively by Suspense. When a suspended promise resolves, React calls resolveRetryWakeable, which calls requestRetryLane to claim the next available retry lane and then calls scheduleUpdateOnFiber on the Suspense boundary fiber.
claimNextRetryLane() round-robins through four dedicated retry lane slots.includesOnlyRetries(lanes) and includesRetryLane(lanes, lane) in ReactFiberLane.js let the work loop identify renders that are purely retrying suspended work.alwaysThrottleRetries (a feature flag) controls whether retry renders are throttled via FALLBACK_THROTTLE_MS.The SomeRetryLane constant is used in ReactFiberCompleteWork.js when scheduling a retry effect on a Suspense boundary packages/react-reconciler/src/ReactFiberCompleteWork.js176-180
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js850-862 packages/react-reconciler/src/ReactFiberCompleteWork.js176-180
OffscreenLaneOffscreenLane is the lowest-priority lane. It is assigned to hidden subtrees (inside <Activity mode="hidden"> or <Offscreen mode="hidden">) and to pre-rendered content. When a fiber is hidden, its child work is assigned to OffscreenLane via laneToLanes(OffscreenLane) packages/react-reconciler/src/ReactFiberBeginWork.js711
requestDeferredLane and useDeferredValuerequestDeferredLane() is called by the hooks implementation when useDeferredValue defers an update. It returns either OffscreenLane (during pre-rendering) or a transition lane claimed by claimNextTransitionDeferredLane() packages/react-reconciler/src/ReactFiberWorkLoop.js864-901
The workInProgressDeferredLane variable ensures all useDeferredValue hooks in a single render share one lane, batching their deferred work.
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js864-901 packages/react-reconciler/src/ReactFiberBeginWork.js705-727
markRootFinishedAt the end of a successful commit, markRootFinished(root, finishedLanes, remainingLanes) is called. It:
finishedLanes from pendingLanes, suspendedLanes, pingedLanes, and expiredLanes.expirationTimes entries for the finished lanes.entanglements for the finished lanes.movePendingFibersToMemoized(root, finishedLanes) to update the lane-to-fiber map used by updater tracking.Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js204
The ReactEventPriorities.js module acts as the bridge between the React lane model and the scheduler package's priority model.
Key functions:
eventPriorityToLane(priority) — converts an event priority number to a lane constant, used in requestUpdateLane packages/react-reconciler/src/ReactFiberWorkLoop.js847lanesToEventPriority(lanes) — used by the root scheduler to pick a scheduler callback priority.resolveUpdatePriority() — provided by the host config (e.g., the DOM renderer tracks the current event's priority).setCurrentUpdatePriority() / getCurrentUpdatePriority() — called around event handlers by the event system to establish the ambient priority.Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js222-228 packages/react-reconciler/src/ReactFiberWorkLoop.js104-127
The following diagram shows the full path of a lane from update scheduling through commit.
Sources: packages/react-reconciler/src/ReactFiberWorkLoop.js804-848 packages/react-reconciler/src/ReactFiberWorkLoop.js200-221 packages/react-reconciler/src/ReactFiberReconciler.js353-458
Refresh this wiki