React Fiber Reconciler: How React Schedules and Commits Updates
How to read this page
Symvanta parsed this repository into a code graph: every function, class, and method is a node, and every call or import between them is an edge. Everything on this page is computed from that graph at the commit shown above. This page is a deep dive into one subsystem of React Architecture: How the Codebase Actually Works; the hub page holds the whole-repo module map and glossary.
The Fiber reconciler is the part of React that decides what changed and when to apply it. Symvanta's Louvain community detection groups it into a single cluster, React Fiber Core, at 1,928 symbols the second-largest product cluster in the repository after the React Compiler's HIR, anchored by the Fiber type itself. Every renderer React ships (react-dom, react-native-renderer, react-test-renderer, react-art) drives the same reconciler code; only the host config that turns fiber effects into real mutations differs. This is why a single package owns both the scheduler that prioritizes work and the commit engine that writes it out.
The reconciler's job splits cleanly in two. The render phase builds a work-in-progress tree of Fiber nodes and can be paused, aborted, or restarted at a higher priority. The commit phase takes a completed tree and applies it to the host in one synchronous, uninterruptible pass. The data structures below are what make that interruptibility possible: React keeps two copies of every fiber and never mutates the visible tree until the whole render is finished. This analysis is generated from Symvanta's code graph over facebook/react at commit f598ec1.
The moving parts
FiberFiberRootLanescheduleUpdateOnFiberperformWorkOnRootbeginWorkcompleteWorkcommitRootrenderWithHooks
A Fiber is one unit of work: a single object with roughly thirty fields spanning lines 89 to 210, carrying the component type, the stateNode (the host instance or class instance), the return, child, and sibling pointers that form the tree, a memoizedState field (the head of the hook linked list for function components), a lanes bitmask of pending work, flags and subtreeFlags effect bitfields, and an alternate pointer to its paired copy. That alternate is the whole trick: every fiber that updates gets a work-in-progress twin, so React can build the next tree without touching the one currently on screen. The FiberRoot sits above the tree; its current pointer names whichever of the two trees is live, and the swap between them is a single assignment at commit time. A Lane is a single bit in a 31-bit priority set; Lanes is a set of them, and the reconciler uses lane bitmasks everywhere to decide which pending updates a given render should include and which to defer. renderWithHooks is the seam every renderer shares: it installs the hooks dispatcher and calls the component function, the bridge between the reconciler and the hooks dispatcher.
How it works
Here is the canonical path a state update travels, from a component calling setState (a bound dispatchSetState) through the pixels changing on screen.
dispatchSetStateis the functionuseStatereturns, bound to the fiber and its update queue. It asksrequestUpdateLane(fiber)for a priority lane, then hands off todispatchSetStateInternal, which enqueues an update record. When the queue is empty it eagerly computes the next state first and bails out entirely if the value is unchanged, so an unchangedsetStatenever schedules a render.scheduleUpdateOnFiberis where a real update enters the work loop. It callsmarkRootUpdatedto merge the new lane into the root's pending set, walks thereturnpointers to propagatechildLanesup to the root, and then callsensureRootIsScheduled.ensureRootIsScheduledregisters the root with the root scheduler so a task is queued (via the Scheduler package for concurrent work, or a microtask for sync work). Multiple updates in the same tick collapse into one scheduled render here.performWorkOnRootis the entry point the scheduled task calls. It choosesrenderRootSyncorrenderRootConcurrentbased on the lanes and whether the work can be time-sliced, then drives the render phase to completion before finishing with a commit.beginWorkruns on the way down. The work loop (workLoopSyncorworkLoopConcurrent) calls it once per fiber, top-down; it reconciles children against the previous tree, and for a function component it callsrenderWithHooksto run the component body and diff its output into child fibers.completeWorkruns on the way back up, once a fiber has no more children to descend into. It creates or updates the host instance (a DOM node, for react-dom), bubbles each fiber'sflagsinto its parent'ssubtreeFlags, and returns to the sibling or parent, so the loop knows in one bitmask read whether a subtree has any commit work at all.commitRoottakes over once the work-in-progress tree is complete. It runs the before-mutation snapshot, thencommitMutationEffects(which inserts, updates, and deletes host nodes), then flipsroot.currentto the finished tree, thencommitLayoutEffects(which firesuseLayoutEffectand attaches refs against the now-current tree). This whole pass is synchronous and cannot be interrupted.flushPassiveEffectsis the deferred tail. Passive effects (useEffectcleanups and callbacks) are scheduled during commit but run later, off the critical path, which is what makesuseEffectfire after paint whileuseLayoutEffectfires before it.
Where it connects
The reconciler cluster is the hub the rest of React reaches through. On the module map, React Fiber Core (hub Fiber) carries the single heaviest cross-module edge in the whole codebase, 452 calls into the React Rendering Utilities (__DEV__) cluster, and receives the single heaviest edge pointing at any cluster: 295 calls from the react-dom host layer (Rendering Components, hub Instance), the seam where fiber effects become real DOM mutations. React DevTools reads reconciler state heavily too, 75 calls from the element-inspection cluster (ElementType) and 11 from the timeline geometry (Rect), and Flight (ReactComponentInfo) reaches in 37 times where a resolved server payload becomes fiber updates on the client.
The tightest coupling is with hooks. renderWithHooks lives inside this cluster and is the exact point where the reconciler installs a dispatcher and calls a component; the sibling hooks dispatcher spoke picks the story up there, following a single useState call through mount and re-render. Every hook's state, the memoizedState linked list described above, hangs off the very Fiber objects the render phase walks.
By the numbers
Symvanta counts 1,928 symbols in the React Fiber Core cluster. The Fiber type alone spans 121 lines (89 to 210) as one flat object, deliberately merged into a single allocation rather than split across intersected types. commitRoot runs 197 lines (3,711 to 3,908) of synchronous commit orchestration. The reconciler also forms one of the repository's 16 dependency cycles, a 90-file cycle across the render and commit modules that call back and forth, the third-largest cycle in the codebase behind DevTools' view layer and the React Compiler's reactive-scope passes.
Auto-generated by Symvanta from the public repo facebook/react at commit f598ec1 , licensed MIT .