Inside React: What the Reconciler Actually Decides

ContextDx logoContextDx
Build something like this?
Insights1

Architecture dossier

Inside React: What the Reconciler Actually Decides

React monorepo (yarn workspaces, packages/*). Layered architecture: a renderer-agnostic Fiber reconciler driven by the core React API, fanned out to multiple host renderers (DOM, Native, ART, test), with a parallel Server Components / streaming stack (Flight + Fizz), plus DevTools, developer tooling, and shared internals.

5 findings · 1 traced path across this system.

Future-Readiness — What would have to break for external state reads — 2026-06-02

5 insights · 1 path

Future-Readiness — What would have to break for an external state-read primitive to exist

Framing

The absence is not an oversight; it is a load-bearing boundary. Three invariants would each have to break — and they are not equally reversible.

Seam 1 — the render-only dispatcher gate (large, additive, partially reversible)

Every read funnels through ReactSharedInternals.H, live only between renderWithHooks and finishRenderingHooks (ReactFiberHooks.js:511,656,664). A read-from-outside API needs a render-independent read path. First safe step: never expose raw fiber reads — expose committed-tree snapshots built on the existing useSyncExternalStore consistency machinery, never in-flight WIP state.

Seam 2 — concurrency makes 'the state' multi-valued (IRREVERSIBLE)

Double buffering (fiber.alternateReactInternalTypes.js:174; createWorkInProgressReactFiber.js:327-355) plus the scheduler's interruptible lanes mean committed and in-flight values are simultaneously live. Any external read must choose committed-vs-in-flight semantics or tear. This is the load-bearing wall — it is the same property that makes time-slicing, Suspense, transitions, and Offscreen safe. Exposing uncontrolled reads would forfeit React's ownership of when reads happen, and could not be walked back.

Seam 3 — positional hook identity (epic, breaking, but local)

Hooks are addressed by call order, not by key (ReactFiberHooks.js:194-200,263-266). There is no name to read by. Keyed identity would mean changing the hook model itself. Don't retrofit hooks — the keyed, addressable path already exists as useSyncExternalStore.

The safe seam already exists and points the other way

useSyncExternalStore (:1633) is React's expand-contract bridge: snapshot per render + checkIfSnapshotChanged forcing a sync re-render on tearing (:1846-1850). react-cache was an earlier experiment in the same spirit. The reversible evolution is to keep state outside the tree and enrich the doorway (selectors, derived snapshots, transition-aware reads) — not to drill a hole into fiber internals.

Ranked roadmap (by reversibility)

SeamEffortReversibilityVerdict
Hook identity (keyed)EpicIrreversible (local API)Avoid — use external stores
Dispatcher gate (2nd read path)LargePartially reversible (additive)Only as committed-snapshot reads
Concurrency read-ownershipIrreversible (global)Do not break — it is the foundation

Suggestion

Add an explicit dispatcher-bridge edge react ⇒ react-reconciler to the board. The two packages have no direct import edge — they are joined only by the mutable ReactSharedInternals.H slot during render. Making that runtime bridge visible explains at a glance why reads are render-gated and why an external primitive has nowhere to attach.

Trace a path on the diagram

What an external-read primitive would have to cross

The three structural barriers, in increasing order of irreversibility, that any 'read state from outside' API would have to break through.

  1. 1.Where such a primitive would live — the public surface
  2. 2.Must bypass the render-only dispatcher gate (seam 1, large/additive)
  3. 3.Must define semantics against concurrent lanes — the irreversible wall

risk1

Seam 2 (irreversible) — concurrency makes 'the state' multi-valued

riskhighverified

Double buffering (fiber.alternate, ReactInternalTypes.js:174; createWorkInProgress, ReactFiber.js:327-355) plus the scheduler's interruptible lanes mean a committed value and one or more in-flight values are simultaneously live. Any external-read primitive must pick committed-vs-in-flight semantics or accept tearing.

Impact: This is entangled with time-slicing, Suspense, transitions, and Offscreen. Exposing uncontrolled reads would forfeit the guarantee that React decides when reads happen — the precondition for all concurrent features.

This is the irreversible decision. The reason there is no out-bound read is the same reason concurrent rendering is safe: React owns read timing. A primitive that read in-flight state could not be walked back without breaking concurrency guarantees that the whole ecosystem now depends on.

concurrencytearingirreversiblelanes

opportunity3

The safe seam already exists and points the other way

opportunityhighverifiedmedium

useSyncExternalStore (ReactFiberHooks.js:1633) is React's expand-contract bridge for external state: snapshot read each render + checkIfSnapshotChanged forcing a sync re-render on tearing (1846-1850). react-cache was an earlier external-held experiment in the same spirit. The evolution path is not 'expose fiber state' but 'keep state outside and make the doorway richer.'

Impact: Investing here delivers external readability with zero risk to concurrency, because React still controls every read.

Treat useSyncExternalStore as the canonical external-state boundary. Future readability features (selectors, derived snapshots, transition-aware reads) belong on this seam — it is reversible (expand-contract) where a fiber-read API is not.

use-sync-external-storeexpand-contractsafe-seam

Seam 1 — the render-only dispatcher gate would have to gain a second read path

opportunitymediumverifiedlarge

Today every read funnels through ReactSharedInternals.H, set only inside renderWithHooks and nulled at finishRenderingHooks (ReactFiberHooks.js:511,656,664). For an external read primitive to exist, the fiber would need a render-independent read path that does not depend on the dispatcher slot being live.

Impact: A render-independent read path is the minimum architectural surface required; without it 'read state outside render' is structurally a thrown error.

First safe step: do NOT add a raw fiber-read API. Instead expose committed-tree snapshot reads built on the existing useSyncExternalStore consistency machinery, and never surface in-flight work-in-progress state. This keeps React the owner of when reads happen.

seamdispatcherevolution

Seam 3 — positional hook identity would have to become addressable

opportunitymediumverifiedepic

Hooks are addressed by call order, not by key (ReactFiberHooks.js:194-200, 263-266). There is no name to read state by. An external primitive needs stable, keyed identity — which the positional linked-list model cannot provide without changing the hook model itself.

Impact: Retrofitting keys onto positional hooks would be an epic, breaking change to the most-used API in the library.

Don't retrofit hooks. The addressable-state path already exists: useSyncExternalStore gives external stores their own keyed identity. Formalize and enrich the external-store boundary rather than making fiber internals addressable.

seamhooksidentitykeyed-state

observation1

What would have to break — the three invariants

observationhighinferred

An external state-read primitive requires breaking three invariants at once: (1) the render-only dispatcher gate, (2) positional hook identity, and (3) React's ownership of read timing under concurrency. The first two are hard; the third is the one that cannot be unwound without forfeiting time-slicing safety.

Ranked by reversibility: identity (irreversible API change, but local), gate (large, additive), concurrency ownership (the load-bearing wall). Because (3) is exactly what makes Suspense/transitions/Offscreen possible, the rational evolution is to keep state outside the tree and enrich useSyncExternalStore — which is precisely what React has done.

summaryinvariantsroadmap

Want this view of your own system?

ContextDx maps your architecture from your codebase and reconciles it into living, shareable insights — just like this board.