Open-Source Wikis

/

React

/

Packages

/

react-dom

facebook/react

react-dom

Active contributors: gnoff, sebmarkbage, eps1lon, sophiebits, acdlite

Purpose

packages/react-dom/ is the browser DOM renderer — the package most React users actually import. It exposes the client mount API (createRoot, hydrateRoot), the streaming SSR drivers (react-dom/server.node, react-dom/server.edge, react-dom/server.browser, react-dom/server.bun), the static prerender drivers (react-dom/static.*), and the react-dom/client re-exports for flushSync/unstable_batchedUpdates/etc.

Most of the heavy lifting is not in this package. The DOM-specific reconciler host config — createInstance, appendChild, attribute setting, the synthetic event system, hoisting of <title>/<link>/etc., suspense-aware resource preloading — lives in the sibling package packages/react-dom-bindings/. react-dom re-exports a small surface that delegates to react-dom-bindings and react-reconciler.

Directory layout

packages/react-dom/
├── package.json                              # 30+ entry points
├── index.js / client.js / profiling.js
├── server.js / server.node.js / server.edge.js / server.browser.js / server.bun.js
├── static.js / static.node.js / static.edge.js / static.browser.js
├── unstable_server-external-runtime.js
├── unstable_testing.js
├── test-utils.js
└── src/
    ├── ReactDOMSharedInternals.js            # the dispatcher + currentDispatcher
    ├── ReactDOMFB.js / ReactDOMFB.modern.js  # Facebook-internal surface
    ├── ReactDOMReactServer.js                # the server condition surface
    ├── client/
    │   ├── ReactDOMClient.js                 # createRoot / hydrateRoot
    │   ├── ReactDOMRoot.js                   # the implementation
    │   └── ReactDOMDefaultTransitionIndicator.js
    ├── server/                               # streaming SSR public API
    │   ├── ReactDOMFizzServerNode.js         # renderToReadableStream / renderToPipeableStream
    │   ├── ReactDOMFizzServerEdge.js
    │   ├── ReactDOMFizzServerBrowser.js
    │   ├── ReactDOMFizzServerBun.js
    │   ├── ReactDOMFizzStaticNode.js / Edge.js / Browser.js  # prerender / resume
    │   └── ReactDOMServerLegacy.js (deprecated)
    ├── events/                               # public event-handling helpers (act, etc.)
    ├── shared/                               # shared with react-dom-bindings
    └── test-utils/                           # exported test-only helpers

packages/react-dom-bindings/
└── src/
    ├── client/
    │   ├── ReactFiberConfigDOM.js            # the host config (createInstance, ...) — ~3,000 lines
    │   ├── ReactDOMComponent.js              # diffProperties, setInitialDOMProperties
    │   ├── ReactDOMHostContext.js
    │   ├── ReactDOMRoots.js / ReactDOMContainer.js
    │   ├── ReactDOMSelection.js
    │   ├── ReactInputSelection.js
    │   └── ... (CSPropertyOperations, CSSPropertyOperations, etc.)
    ├── server/
    │   ├── ReactFizzConfigDOM.js             # the Fizz host config
    │   ├── ReactFizzConfigDOMLegacy.js
    │   └── ReactDOMFloatServer.js / ReactDOMResources.js  # hoisting <link>, <title>, etc.
    ├── events/                               # the synthetic event system
    │   ├── ReactDOMEventListener.js
    │   ├── DOMPluginEventSystem.js
    │   └── plugins/                          # SimpleEventPlugin, ChangeEventPlugin, ...
    └── shared/

Key abstractions

Abstraction File Description
createRoot(container, options?) packages/react-dom/src/client/ReactDOMRoot.js Returns a Root with .render(element) and .unmount(). Internally calls createContainer from the reconciler.
hydrateRoot(container, element, options?) packages/react-dom/src/client/ReactDOMRoot.js Same idea, but ties to existing server-rendered DOM.
flushSync(fn) re-exported from react-reconciler Runs fn and flushes any updates synchronously.
renderToReadableStream(element, options?) packages/react-dom/src/server/ReactDOMFizzServerNode.js (and Edge/Browser/Bun) Streaming SSR. Returns a ReadableStream of HTML.
renderToPipeableStream(element, options?) packages/react-dom/src/server/ReactDOMFizzServerNode.js Node-only variant returning { pipe, abort }.
prerender(element, options?) packages/react-dom/src/server/ReactDOMFizzStaticNode.js (and Edge/Browser) Static prerender. May return a postponed state.
resume(element, postponedState, options?) same files Resumes a prerender that was previously postponed. New in 19.2.
resumeAndPrerender(...) same Variant that resumes and produces final HTML.
prefetchDNS(href) / preconnect(href) / preload(href, options) / preinit(href, options) / preinitModule packages/react-dom-bindings/src/client/ReactDOMFloatClient.js Resource hints. The matching server functions are in the float server file.
Host config packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js The set of functions the reconciler calls into to mutate the DOM.
Synthetic events packages/react-dom-bindings/src/events/ Pluggable event system: SimpleEventPlugin, ChangeEventPlugin, EnterLeaveEventPlugin, SelectEventPlugin, BeforeInputEventPlugin.

How it works

graph LR
  UserCode[root.render &lt;App/&gt;] --> ReactDOMRoot[ReactDOMRoot.js]
  ReactDOMRoot -->|updateContainer| Reconciler[react-reconciler]
  Reconciler -->|host config calls| HostConfig[react-dom-bindings/ReactFiberConfigDOM.js]
  HostConfig -->|createElement, appendChild, ...| DOM[(window.document)]
  Reconciler -->|event priority| EventSys[react-dom-bindings/events/*]
  EventSys -->|dispatch| UserCode

The DOM event system is renderer-aware: it asks the reconciler for the current event priority before dispatching, so a click becomes SyncLane work, a scroll becomes InputContinuousLane work, etc. See packages/react-dom-bindings/src/events/ReactDOMEventListener.js and the priority mapping in packages/react-reconciler/src/ReactEventPriorities.js.

Resource hoisting (Float)

The hoisting system promotes certain elements from inside the component tree into <head>:

  • <title>, <meta>, <link rel="stylesheet">, <link rel="preload">, <script async>.

The client side is packages/react-dom-bindings/src/client/ReactDOMFloatClient.js; the server side is packages/react-dom-bindings/src/server/ReactDOMFloatServer.js. Hoisting is coordinated with Suspense — boundaries that suspend block their resources so the page doesn't show partial state.

Two server engines, one package

Historically react-dom had two server engines:

  1. The legacy synchronous ReactDOMStringRenderer (deprecated and removed).
  2. Fizz — the streaming engine. Fizz lives in packages/react-server/src/ReactFizzServer.js; react-dom/server.* are thin platform-specific drivers that supply Fizz with a host config (packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js) and a stream config (packages/react-server/src/ReactServerStreamConfigNode.js, Edge.js, Browser.js, Bun.js).

The prerender/resume pair extends Fizz with a "postpone here" primitive that lets you split a render across machines or requests.

Integration points

  • react-reconcilerreact-dom calls Reconciler({...DOMHostConfig}) exactly once at module load. The DOM host config is built up from react-dom-bindings/src/client/ReactFiberConfigDOM.js plus the various capability shims (ReactFiberConfigWithNoMutation is not used here — DOM uses mutation mode).
  • react-server — the SSR drivers in react-dom/src/server/ import ReactFizzServer.js from react-server.
  • scheduler — used indirectly via react-reconciler's Scheduler.js shim. The DOM event system asks the scheduler for getCurrentTime and unstable_NormalPriority.
  • reactreact-dom-bindings installs its dispatcher into ReactSharedInternals during render and removes it after.

Entry points for modification

  • Adding an attribute or property handler: packages/react-dom-bindings/src/client/ReactDOMComponent.js is where the big setInitialProperties/updateDOMProperties switches live, plus the boolean-attribute table (packages/react-dom-bindings/src/shared/DOMProperty.js). The matching server code is in packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js.
  • Adding a new resource type to hoist: edit both ReactDOMFloatClient.js and ReactDOMFloatServer.js, and update the matching ReactFizzConfigDOM.js server emit.
  • Changing event behavior: the dispatcher is ReactDOMEventListener.js; per-event-name behavior is the plugin in react-dom-bindings/src/events/plugins/.
  • Adding a new server entry point (e.g. a new platform): add a new ReactDOMFizzServer<Platform>.js and a matching ReactServerStreamConfig<Platform>.js.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

react-dom – React wiki | Factory