Lanes & Priority
Lanes are the reconciler’s bitmask representation of pending work: each bit identifies a scheduling category, while a combined mask represents several categories at once. The model provides 31 lane positions and explicit empty values through TotalLanes, NoLanes, and NoLane.
This exists because concurrent rendering must choose work without losing other pending updates. Bitwise operations can merge, intersect, remove, and test lane sets cheaply, while priority ordering remains encoded by the bit positions.
Sources: packages/react-reconciler/src/ReactFiberLane.js:41, packages/react-reconciler/src/ReactFiberLane.js:43, packages/react-reconciler/src/ReactFiberLane.js:44, packages/react-reconciler/src/ReactFiberLane.js:784-786, packages/react-reconciler/src/ReactFiberLane.js:792-794, packages/react-reconciler/src/ReactFiberLane.js:796-798, packages/react-reconciler/src/ReactFiberLane.js:800-802, packages/react-reconciler/src/ReactFiberLane.js:810-813
Core concepts
A lane is one scheduling bit
A lane is a single bit such as SyncLane, DefaultLane, or TransitionLane1; getHighestPriorityLane selects the lowest set bit from a lane mask.
Sources: packages/react-reconciler/src/ReactFiberLane.js:47, packages/react-reconciler/src/ReactFiberLane.js:54, packages/react-reconciler/src/ReactFiberLane.js:63, packages/react-reconciler/src/ReactFiberLane.js:756-758
A lane set is a bitmask
A lane set is a mask containing one or more lanes, so mergeLanes combines pending work with bitwise OR and isSubsetOfLanes checks whether one set is contained in another.
Sources: packages/react-reconciler/src/ReactFiberLane.js:788-790, packages/react-reconciler/src/ReactFiberLane.js:792-794
Priority is encoded by bit position
Priority is not stored as a separate numeric field: higherPriorityLane compares lane bits directly, relying on the rule that bit ranges decrease in priority from right to left.
Sources: packages/react-reconciler/src/ReactFiberLane.js:810-813
An entanglement is a batching constraint
An entanglement means one lane cannot render in a batch without another lane, allowing related updates to be processed together even when their individual priorities differ.
The lane catalog groups work into several scheduling families rather than one flat priority number.
| Lane family | Examples | Purpose |
|---|---|---|
| Blocking work | SyncLane, InputContinuousLane, DefaultLane | Represents synchronous, continuous-input, and default updates. |
| Transitions | TransitionLane1 through TransitionLane10 | Provides multiple rotating lanes for transition updates. |
| Deferred transitions | TransitionLane11 through TransitionLane14 | Reserves lanes for deferred transition work. |
| Suspense retries | RetryLane1 through RetryLane4 | Represents retry work associated with suspended content. |
| Idle and deferred work | IdleLane, OffscreenLane, DeferredLane | Represents work that is lower priority than non-idle work. |
The family masks are assembled from the individual bits: TransitionUpdateLanes contains the first ten transition lanes, TransitionDeferredLanes contains the last four, and RetryLanes contains the retry lanes.
Sources: packages/react-reconciler/src/ReactFiberLane.js:438-449, packages/react-reconciler/src/ReactFiberLane.js:80-90, packages/react-reconciler/src/ReactFiberLane.js:91-92, packages/react-reconciler/src/ReactFiberLane.js:94, packages/react-reconciler/src/ReactFiberLane.js:104, packages/react-reconciler/src/ReactFiberLane.js:107, packages/react-reconciler/src/ReactFiberLane.js:109, packages/react-reconciler/src/ReactFiberLane.js:110
How pending work becomes the next render
When a root has pending work, getNextLanes first reads root.pendingLanes, root.suspendedLanes, root.pingedLanes, and root.warmLanes. It returns NoLanes immediately when there is no pending work.
For non-idle work, the selection order is: fresh lanes that are not suspended, suspended lanes that have been pinged, and finally lanes that can be prewarmed when the root has no pending commit. Each candidate set is reduced through getHighestPriorityLanes.
Idle work is considered only when there is no pending non-idle work. This prevents idle work from running while regular work remains, even if that regular work is suspended.
The call order below shows how a pending mask is reduced to the next render mask.
Evidence
- root-statepackages/react-reconciler/src/ReactFiberLane.js:255
- lane-selectorpackages/react-reconciler/src/ReactFiberLane.js:249
- priority-selectorpackages/react-reconciler/src/ReactFiberLane.js:180
getHighestPriorityLanes first preserves all pending synchronous update lanes in SyncUpdateLanes; otherwise it switches on getHighestPriorityLane and returns the appropriate priority group, such as one transition lane, all transition update lanes, retry lanes, or an idle lane.
An in-progress render is not interrupted merely because another lane is pending. getNextLanes compares the highest lane in the new selection with the highest lane already being rendered and keeps the existing work when the new work is equal or lower priority; default work also does not interrupt transition work.
Sources: packages/react-reconciler/src/ReactFiberLane.js:249-265, packages/react-reconciler/src/ReactFiberLane.js:279-301, packages/react-reconciler/src/ReactFiberLane.js:279-311, packages/react-reconciler/src/ReactFiberLane.js:180-225, packages/react-reconciler/src/ReactFiberLane.js:56-57, packages/react-reconciler/src/ReactFiberLane.js:334-360
How multiple updates share one render
Multiple updates are batched by carrying multiple bits in the same render mask. For synchronous flushing, getNextLanesToFlushSync starts with SyncUpdateLanes, adds any forced lanes, removes suspended-but-not-pinged lanes, and includes every unblocked lane with equal or higher priority.
Entanglement adds another batching rule. getEntangledLanes begins with the render lanes, includes the default lane when continuous input work is present, and then follows the root’s entanglement map to add lanes that must render together.
This means a render can have a task priority distinct from the full set of lanes it carries. The work-loop excerpt describes a transition rendering together with a suspended SyncLane while the batch still runs at transition priority.
Updates are also assigned across a rotating pool rather than being forced into one transition bit. claimNextTransitionUpdateLane advances through TransitionUpdateLanes and wraps back to TransitionLane1; deferred transition and retry claims use the same rotation pattern for their respective masks.
When a root receives an update, markRootUpdated ORs the update lane into root.pendingLanes. For non-idle updates, it also clears suspended, pinged, and warm lanes so the root can try the affected work again.
Sources: packages/react-reconciler/src/ReactFiberLane.js:363-406, packages/react-reconciler/src/ReactFiberLane.js:427-474, packages/react-reconciler/src/ReactFiberWorkLoop.js:2275-2284, packages/react-reconciler/src/ReactFiberLane.js:726-736, packages/react-reconciler/src/ReactFiberLane.js:738-745, packages/react-reconciler/src/ReactFiberLane.js:747-754, packages/react-reconciler/src/ReactFiberLane.js:825-848
How starvation is prevented
Starvation protection is time-based. markStarvedLanesAsExpired scans pending lanes, assigns an expiration time to eligible lanes that do not have one, and marks lanes whose expiration time has arrived in root.expiredLanes.
Expiration policy differs by lane family. Interaction-oriented lanes receive a shorter expiration, default and transition lanes receive a transition expiration, retry lanes expire only when enableRetryLaneExpiration allows it, and idle-or-lower lanes return NoTimestamp so they do not expire.
This prevents a pending non-idle update from waiting forever behind repeated work: once its expiration time is reached, the lane is recorded as expired and can be recognized separately from ordinary blocking priority. includesExpiredLane explicitly checks root.expiredLanes because a lane may expire after rendering has already started.
Suspension does not permanently discard a lane. markRootSuspended records suspended lanes, clears their pinged state, and clears their expiration timestamps; markRootPinged later restores matching suspended lanes to root.pingedLanes when data resolves.
After a render finishes, markRootFinished replaces root.pendingLanes with the remaining lanes and clears suspended, pinged, and warm state. It also removes bookkeeping for lanes that no longer have pending work.
Sources: packages/react-reconciler/src/ReactFiberLane.js:541-584, packages/react-reconciler/src/ReactFiberLane.js:477-530, packages/react-reconciler/src/ReactFiberLane.js:696-700, packages/react-reconciler/src/ReactFiberLane.js:851-880, packages/react-reconciler/src/ReactFiberLane.js:887-892, packages/react-reconciler/src/ReactFiberLane.js:894-934
How it connects
Event priority is converted directly into a reconciler lane: eventPriorityToLane returns the event priority as a Lane, while resolveUpdatePriority derives the current priority from an explicit value or the browser event type.
The work loop consumes lane selections to drive rendering, including entangled lanes and concurrent update queue completion before work begins.
Update queues use lanes to decide which updates participate in the current render. Updates remain insertion-ordered; insufficient-priority updates stay queued, and later updates remain queued behind them so skipped work can be rebased during a lower-priority render.
For hydration, the reconciler can map a render lane to a corresponding hydration lane, such as SyncLane to SyncHydrationLane or DefaultLane to DefaultHydrationLane.
Read this page alongside Fiber Architecture for the root and fiber state that stores lanes, Work Loop & Scheduling for how selected lanes drive rendering, and Concurrent Features & Transitions for how transition updates enter the lane model.
Sources: packages/react-reconciler/src/ReactEventPriorities.js:30-52, packages/react-dom-bindings/src/client/ReactDOMUpdatePriority.js:31-45, packages/react-reconciler/src/ReactFiberWorkLoop.js:2275-2292, packages/react-reconciler/src/ReactFiberClassUpdateQueue.js:48-58, packages/react-reconciler/src/ReactFiberLane.js:1116-1157
Key takeaways
- A lane is one bit; a lane mask can represent several pending update categories.
- Bit positions encode priority, while bitwise operations preserve batching and set relationships.
getNextLanesprefers unblocked higher-priority work and avoids interrupting equal-or-lower-priority progress.- Entanglement forces related lanes into the same render batch.
- Expiration times move starved eligible lanes into root.expiredLanes, preventing indefinite delay.