# Data flow — facebook/react

> Canonical page: https://codevetter.com/unpack/react/data-flow

# Data flow — facebook/react

Scanned commit 59aff3e18cb5b3a336c280bbfa57ec37999511b9 (2026-09-18). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

On the client, input enters through DOM events (delegated at the root by `listenToAllSupportedEvents`) or imperative `root.render`, both of which funnel into `scheduleUpdateOnFiber(root, fiber, lane)`. State lives on the Fiber tree hanging off `FiberRoot`; the `lane` bitmask on `ReactFiberLane.js` is the priority currency that decides whether work is flushed synchronously, in a microtask, or via a Scheduler task. Output boundaries are the host config (`ReactFiberConfig`, substituted per renderer) and, for SSR/RSC, the stream `Destination` in Fizz/Flight. Cross-module state is deliberately routed through a single mutable singleton, `ReactSharedInternals` (`.H` = current hooks dispatcher, `.T` = current transition, `.A` = async dispatcher), which is itself a forked module so client and server get different instances.
- All client updates converge on `scheduleUpdateOnFiber`, which marks the root (`markRootUpdated(root, lane)`), merges interleaved/render-phase lanes, and calls `ensureRootIsScheduled(root)`; sync updates on legacy roots additionally trigger `flushSyncWorkOnLegacyRootsOnly()` immediately. — [packages/react-reconciler/src/ReactFiberWorkLoop.js#L987-L1110](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberWorkLoop.js#L987-L1110)
- `ensureRootIsScheduled` does not schedule work directly — it appends the root to an intrusive linked list (`firstScheduledRoot`/`lastScheduledRoot` via `root.next`), sets `mightHavePendingSyncWork = true`, and defers to a microtask that runs `processRootScheduleInMicrotask` → `scheduleTaskForRootDuringMicrotask`. — [packages/react-reconciler/src/ReactFiberRootScheduler.js#L116-L170](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberRootScheduler.js#L116-L170) [packages/react-reconciler/src/ReactFiberRootScheduler.js#L259-L513](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberRootScheduler.js#L259-L513)
- Priority is encoded as a 31-bit lane bitmask (`TotalLanes = 31`) with named lanes `SyncLane`, `InputContinuousLane`, `DefaultLane`, transition lanes, retry lanes, `IdleLane`, `OffscreenLane` and `DeferredLane`; `requestUpdateLane(fiber)` picks the lane for each update. — [packages/react-reconciler/src/ReactFiberLane.js#L41-L110](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberLane.js#L41-L110) [packages/react-reconciler/src/ReactFiberWorkLoop.js#L812-L884](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberWorkLoop.js#L812-L884)
- `performWorkOnRoot` decides between `renderRootConcurrent` and `renderRootSync` from `shouldTimeSlice`, which is false for blocking lanes, expired lanes, or `forceSync` — but forced true when `checkIfRootIsPrerendering(root, lanes)` so prerendering never blocks the main thread. — [packages/react-reconciler/src/ReactFiberWorkLoop.js#L1163-L1182](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberWorkLoop.js#L1163-L1182)
- External-store consistency is re-validated after a concurrent render: if `isRenderConsistentWithExternalStores(finishedWork)` fails, `performWorkOnRoot` discards the result and re-runs `renderRootSync(root, lanes, false)` to block further mutation. — [packages/react-reconciler/src/ReactFiberWorkLoop.js#L1218-L1245](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberWorkLoop.js#L1218-L1245)
- Hook state is read and written through a swappable dispatcher on the shared-internals singleton: `renderWithHooks` assigns `ReactSharedInternals.H` to `HooksDispatcherOnMount` or `HooksDispatcherOnUpdate` (DEV variants included) and resets it to `ContextOnlyDispatcher` when the component finishes. — [packages/react-reconciler/src/ReactFiberHooks.js#L505-L570](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberHooks.js#L505-L570) [packages/react-reconciler/src/ReactFiberHooks.js#L655-L665](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberHooks.js#L655-L665) [packages/shared/ReactSharedInternals.js](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/shared/ReactSharedInternals.js)
- `root.render(children)` writes nothing directly — it calls `updateContainer(children, root, null, null)`, which becomes an update object on the host-root fiber's update queue via `createUpdate(lane)` + `enqueueUpdate(rootFiber, update, lane)`. — [packages/react-dom/src/client/ReactDOMRoot.js#L110-L136](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-dom/src/client/ReactDOMRoot.js#L110-L136) [packages/react-reconciler/src/ReactFiberReconciler.js#L353-L455](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberReconciler.js#L353-L455)
- DOM events are the other input boundary and are delegated once per root: `createRoot` and `hydrateRoot` each call `listenToAllSupportedEvents(container)` from `react-dom-bindings/src/events/DOMPluginEventSystem`, so per-node listeners are not attached. — [packages/react-dom/src/client/ReactDOMRoot.js#L84](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-dom/src/client/ReactDOMRoot.js#L84) [packages/react-dom/src/client/ReactDOMRoot.js#L258](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-dom/src/client/ReactDOMRoot.js#L258) [packages/react-dom/src/client/ReactDOMRoot.js#L357](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-dom/src/client/ReactDOMRoot.js#L357)
- On the server, Fizz's data flow is request-scoped: `createRequest(...)` builds a `Request`, `startWork(request)` renders tasks (`retryTask`, `renderNode`, `renderNodeDestructive`), and `startFlowing(request, destination)` drains completed segments through `flushCompletedQueues` into the stream. — [packages/react-server/src/ReactFizzServer.js#L601-L620](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-server/src/ReactFizzServer.js#L601-L620) [packages/react-server/src/ReactFizzServer.js#L4312-L5340](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-server/src/ReactFizzServer.js#L4312-L5340) [packages/react-server/src/ReactFizzServer.js#L6175-L6470](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-server/src/ReactFizzServer.js#L6175-L6470)
- Server-Action input crosses a trust boundary as untyped `FormData`: `decodeAction` copies every non-`$ACTION_` field into a fresh `FormData`, decodes bound args by replaying them through `createResponse`/`getRoot` in `decodeBoundActionMetaData`, and binds the result — so client-supplied bytes drive `resolveServerReference(bundlerConfig, id)`. — [packages/react-server/src/ReactFlightActionServer.js#L46-L160](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-server/src/ReactFlightActionServer.js#L46-L160)
- `decodeBoundActionMetaData` accepts an `arraySizeLimit` parameter threaded into `createResponse`, indicating the Flight reply parser has an explicit bound on attacker-controlled array sizes. — [packages/react-server/src/ReactFlightActionServer.js#L76-L92](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-server/src/ReactFlightActionServer.js#L76-L92)
- DevTools data flows over a batched message bus: `Bridge` wraps a `Wall`, queues events and flushes them on a `BATCH_DURATION` timer, sending typed `BackendEvents` (`overrideProps`, `overrideHookState`, `overrideState`, `overrideContext`) from frontend to backend. — [packages/react-devtools-shared/src/bridge.js#L207-L230](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/bridge.js#L207-L230) [packages/react-devtools-shared/src/bridge.js#L317-L441](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/bridge.js#L317-L441) [packages/react-devtools-shared/src/bridge.js#L485-L525](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/bridge.js#L485-L525)
- The renderer registers itself with the page-global `__REACT_DEVTOOLS_GLOBAL_HOOK__` (installed by `installHook(target)` with `Object.defineProperty`, and explicitly documented as a de-facto public API) via `renderers.set(id, renderer)` — that global is the whole runtime↔DevTools data channel. — [packages/react-devtools-shared/src/hook.js#L3-L10](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/hook.js#L3-L10) [packages/react-devtools-shared/src/hook.js#L59-L72](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/hook.js#L59-L72) [packages/react-devtools-shared/src/hook.js#L212](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/hook.js#L212) [packages/react-devtools-shared/src/hook.js#L659](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/hook.js#L659)
- In the Rust compiler, data flows as JSON across the FFI boundary: Babel serialises the AST + scope info + options, `compile()` deserialises with `serde_json::Deserializer::disable_recursion_limit()` to survive deeply nested ASTs, and returns a JSON `CompileResult`. — [compiler/packages/babel-plugin-react-compiler-rust/native/src/lib.rs#L11-L70](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/compiler/packages/babel-plugin-react-compiler-rust/native/src/lib.rs#L11-L70) [compiler/packages/babel-plugin-react-compiler-rust/src/bridge.ts#L110-L175](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/compiler/packages/babel-plugin-react-compiler-rust/src/bridge.ts#L110-L175)
- The Rust HIR replaces JS shared-object references with arena indices: `identifiers`/`scopes`/`functions`/`types` vectors on `Environment`, addressed by `Copy` newtype IDs (`IdentifierId`, `ScopeId`, `FunctionId`, `TypeId`), with `HirFunction` deliberately not owning `env`. — [compiler/docs/rust-port/rust-port-architecture.md#L5-L70](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/compiler/docs/rust-port/rust-port-architecture.md#L5-L70)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
