facebook/react
react-reconciler
Active contributors: acdlite, sebmarkbage, gnoff, eps1lon, sophiebits, rickhanlonii
Purpose
packages/react-reconciler/ is the largest package in the runtime and the home of React's fiber engine. Everything renderer-agnostic lives here: the work loop, the lanes priority model, all built-in hook implementations, Suspense, transitions, hydration, the commit phase, error boundaries, and the new view-transition machinery. Renderers (react-dom-bindings, react-native-renderer, react-art, etc.) plug into it by providing a host config — a struct of imperative methods named in packages/react-reconciler/src/ReactFiberConfig.js.
The package is also published independently on npm under the name react-reconciler, intended for people writing their own custom renderers. Its API is intentionally not fully stable; the README warns against it.
Directory layout
packages/react-reconciler/
├── README.md # 350-line "writing a custom renderer" guide
├── package.json
├── index.js
├── reflection.js # findHostInstancesForRefresh and friends
└── src/
├── ReactFiber.js # the Fiber struct + createFiber* helpers
├── ReactFiberRoot.js # FiberRoot (one per createRoot)
├── ReactFiberLane.js # 31-bit lane bitmask, getNextLanes, etc.
├── ReactFiberFlags.js # commit-phase flags (Placement, Update, ...)
├── ReactWorkTags.js # tags identifying fiber kinds
├── ReactInternalTypes.js # central type definitions
│
├── ReactFiberWorkLoop.js # ~5,000 lines: the work loop
├── ReactFiberBeginWork.js # ~4,500 lines: begin half of render
├── ReactFiberCompleteWork.js # ~2,000 lines: complete half of render
├── ReactFiberCommitWork.js # ~5,000 lines: walking effect lists
├── ReactFiberCommitEffects.js # passive / layout effects
├── ReactFiberCommitHostEffects.js # host-mutation walks
├── ReactFiberCommitViewTransitions.js # view transition commit
├── ReactFiberApplyGesture.js # gesture-driven view transitions
│
├── ReactFiberHooks.js # ~4,500 lines: every built-in hook
├── ReactFiberClassComponent.js # the class API code path
├── ReactFiberClassUpdateQueue.js # legacy class setState queue
├── ReactChildFiber.js # children diffing (the "missing key" warner)
│
├── ReactFiberThrow.js # how thrown values bubble
├── ReactFiberThenable.js # use(promise) machinery
├── ReactFiberSuspenseComponent.js # Suspense boundaries
├── ReactFiberSuspenseContext.js
├── ReactFiberHiddenContext.js
├── ReactFiberTransition.js # transition lanes
├── ReactFiberTransitionTypes.js
├── ReactFiberOffscreenComponent.js # Activity (formerly Offscreen)
├── ReactFiberViewTransitionComponent.js
├── ReactFiberTracingMarkerComponent.js
├── ReactFiberAsyncAction.js / ReactFiberAsyncDispatcher.js
├── ReactFiberCacheComponent.js # the cache() RSC primitive
│
├── ReactFiberHydrationContext.js # client-side hydration walk
├── ReactFiberHydrationDiffs.js # mismatch diffing
├── ReactFiberShellHydration.js # selective hydration entry
│
├── ReactFiberRootScheduler.js # what runs when, across roots
├── ReactFiberConcurrentUpdates.js # interleaved-update bookkeeping
├── ReactFiberDevToolsHook.js # publishes events to react-devtools
├── ReactFiberPerformanceTrack.js # Performance API marks for the Profiler
├── ReactFiberErrorLogger.js # console.error formatting for caught errors
├── ReactFiberCallUserSpace.js # wraps user calls (render, effects, ...)
├── ReactFiberHotReloading.js # hot module replacement support
├── ReactFiberMutationTracking.js # mutation tracking for React Compiler
│
├── ReactFiberConfig.js # the host-config interface (resolved per renderer)
├── ReactFiberConfigWithNoMutation.js # capability shim
├── ReactFiberConfigWithNoHydration.js # capability shim
├── ... (more capability shims)
├── forks/ # per-renderer host config wiring
│
├── Scheduler.js # thin wrapper over the published scheduler
├── ReactStrictModeWarnings.js
├── ReactProfilerTimer.js
├── ReactTestSelectors.js # findAllByType / findByText
├── getComponentNameFromFiber.js
├── clz32.js # count-leading-zeros polyfill
└── __tests__/ # the central reconciler test suiteKey abstractions
| Abstraction | File | Description |
|---|---|---|
Fiber |
packages/react-reconciler/src/ReactFiber.js, type in ReactInternalTypes.js |
Per-component-instance node in the WIP/current trees. ~30 fields including tag, key, type, stateNode, child, sibling, return, alternate, flags, lanes, memoizedState, memoizedProps, pendingProps, updateQueue, dependencies. |
FiberRoot |
packages/react-reconciler/src/ReactFiberRoot.js |
One per createRoot call. Owns the current tree, pending tree, lane bitmask, error log, hydration state. |
Lane / Lanes |
packages/react-reconciler/src/ReactFiberLane.js |
31-bit priority bits. SyncLane, InputContinuousLane, DefaultLane, TransitionLane1..N, RetryLane, IdleLane, OffscreenLane, DeferredLane, GestureLane. |
| Work tags | packages/react-reconciler/src/ReactWorkTags.js |
FunctionComponent, ClassComponent, HostComponent, HostText, Fragment, ContextProvider, MemoComponent, LazyComponent, SuspenseComponent, ActivityComponent, ViewTransitionComponent, ... |
| Effect flags | packages/react-reconciler/src/ReactFiberFlags.js |
Placement, Update, Deletion, Hydrating, Snapshot, Passive, Layout, ContentReset, Ref, Visibility, ShouldCapture, DidCapture, Forked, ... |
| Host config | packages/react-reconciler/src/ReactFiberConfig.js (fork-resolved) |
Imperative interface a renderer must implement: createInstance, appendChild, commitUpdate, prepareForCommit, getCurrentEventPriority, etc. |
| Hooks dispatchers | packages/react-reconciler/src/ReactFiberHooks.js |
Six tables: HooksDispatcherOnMount, OnUpdate, OnRerender, plus dev-mode variants. Each implements every built-in hook. |
act() |
packages/react-reconciler/src/ReactFiberAct.js |
The reconciler half of act — flushes work and yields to passive effects. |
How it works
The work loop
graph TD Update[setState / dispatch / root.render] -->|scheduleUpdateOnFiber| RootScheduler[ReactFiberRootScheduler.js] RootScheduler -->|markRootUpdated, ensureRootIsScheduled| Scheduler[scheduler package] Scheduler -->|resume| WorkLoop[performWorkOnRoot] WorkLoop -->|render phase| BeginWork[beginWork per fiber] BeginWork --> CompleteWork[completeWork per fiber] CompleteWork -->|shouldYield?| WorkLoop WorkLoop -->|done with render| CommitRoot[commitRoot] CommitRoot -->|before-mutation| Snapshot[ClassComponent getSnapshotBeforeUpdate] CommitRoot -->|mutation| HostMut[appendChild / removeChild / commitUpdate] CommitRoot -->|layout| LayoutEff[useLayoutEffect / componentDidMount] CommitRoot -->|after paint| Passive[useEffect: flushPassiveEffects]
Two work loops in ReactFiberWorkLoop.js:
performSyncWorkOnRoot— uninterruptible. Used for synchronous lanes (defaultsetStateoutside transitions).performConcurrentWorkOnRoot— callsshouldYield()from the scheduler between fibers. Used for transition lanes, idle lanes, retry lanes, and anything belowSyncLane.
The render phase always builds a fresh WIP tree as alternate of the current tree. The commit phase then swaps current = WIP. This is React's "double buffering" — the current tree is always consistent with what's on screen.
Lanes
packages/react-reconciler/src/ReactFiberLane.js is dense bit-twiddling. The 31 lanes are ordered by priority. Key helpers:
getNextLanes(root, wipLanes)— picks which lanes to render in the next pass, given what's pending on the root and what's already in flight.mergeLanes,removeLanes— bitwise union/diff.pickArbitraryLane—lanes & -lanes, the lowest set bit. The trick that makes "highest priority lane" cheap.markRootUpdated,markRootEntangled,markRootSuspended,markRootFinished— bookkeeping on the FiberRoot.
packages/react-reconciler/src/ReactEventPriorities.js maps DOM events to lanes (e.g. a click is DiscreteEventPriority → SyncLane; a scroll is ContinuousEventPriority → InputContinuousLane).
Hooks
Every built-in hook has six implementations in ReactFiberHooks.js:
| Phase | Description |
|---|---|
| Mount | First render: allocate the hook cell, set initial state. |
| Update | Subsequent render: read previous cell, apply pending updates from queue. |
| Rerender | Same render re-trying after setState during render. |
| MountInDEV / UpdateInDEV / RerenderInDEV | Dev-only variants that record HookType strings for the act warnings and DevTools. |
Hooks share a small set of internal primitives:
mountWorkInProgressHook/updateWorkInProgressHook— allocate or step the linked list of hook cells.dispatchSetStateInternal— the body ofsetStatecalls; computes the lane, marks the fiber updated, schedules.enqueueUpdate— appends to the hook's update queue.useThenable(inReactFiberThenable.js) — backsuse(promise)and the implicit awaits insideuse(...).
Suspense
Three pieces work together:
ReactFiberThrow.js— handlesthrow thenable(suspense) andthrow new Error(...)(error boundary). Walks up the tree marking ancestors withShouldCapture.ReactFiberSuspenseComponent.jsandReactFiberSuspenseContext.js— represent a Suspense boundary, decide whether to show fallback or content, suspend retries.ReactFiberThenable.js— tracks all thenables thrown during a render so that retries can wait on them.
Hydration
ReactFiberHydrationContext.js walks the existing host tree and matches it to fresh React work. There are two flavors:
- Full-tree hydration —
hydrateRoot(container, element). - Selective hydration — Suspense boundaries hydrate as their content arrives via streaming. The reconciler can pause hydration of a low-priority boundary if a click lands on a higher-priority one.
ReactFiberHydrationDiffs.js produces the rich "Server: ... Client: ..." mismatch error message.
Commit
ReactFiberCommitWork.js orchestrates three sub-passes:
- Before-mutation — snapshot DOM state (
getSnapshotBeforeUpdate). - Mutation —
commitMutationEffectsOnFiberwalks the effect list, calling host config methods to apply the diff. - Layout —
useLayoutEffectandcomponentDidMount/componentDidUpdatecallbacks. Synchronous, before the browser paints.
After commit, passive effects (useEffect) are scheduled to flush after paint, via the scheduler's IdlePriority callback in flushPassiveEffects.
Integration points
- Public surface to renderers —
packages/react-reconciler/src/ReactFiberReconciler.jsexports theReconciler({ ...hostConfig })factory used by every renderer. It returns a{ createContainer, updateContainer, getPublicRootInstance, ... }API. - Imports from
react— onlyReactSharedInternals(the dispatcher / current owner) and the symbol set fromshared/ReactSymbols. No directreact↔react-reconcilercycle. - Scheduler — every "yield to the browser" goes through
Scheduler.unstable_scheduleCallback(re-exported viapackages/react-reconciler/src/Scheduler.js). - DevTools —
packages/react-reconciler/src/ReactFiberDevToolsHook.jspublishes commit events (root committed, fiber unmounted, etc.) to the global__REACT_DEVTOOLS_GLOBAL_HOOK__. The hook is installed by the DevTools backend inreact-devtools-shared.
Entry points for modification
- Adding a new fiber tag: add to
ReactWorkTags.js, then add a switch case to everygetComponentNameFromFiber.jsbranch, the begin/complete work switches inReactFiberBeginWork.jsandReactFiberCompleteWork.js, the commit walks inReactFiberCommitWork.js, and the DevTools fiber walker inpackages/react-devtools-shared/src/backend/fiber/renderer.js. - Adding a new flag bit: add to
ReactFiberFlags.js, then auditcommitMutationEffectsOnFiberandcommitLayoutEffectsOnFiberinReactFiberCommitWork.jsfor places that should observe it. - Adding a new lane: edit
ReactFiberLane.js— every helper has to know about it. Be careful — the lane bit positions are part of the priority order. - Adding a new built-in hook: see packages/react.
Related pages
- react — public API and dispatcher routing.
- scheduler — the cooperative task runner.
- features/concurrent-rendering — how lanes, transitions, and the scheduler cooperate.
- features/hooks — the hook lifecycle from
useState(0)to a committed update. - features/suspense — Suspense and Activity primitives.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.