This page documents how bundlers integrate with React Server Components (RSC): how 'use client' and 'use server' directives are detected and transformed at build time, how manifest files are generated, and how the runtime resolves those references across the server/client boundary.
For the underlying Flight serialization protocol and wire format, see 5.2. For server action encoding and the reply protocol, see 5.4. For the Fizz streaming SSR system (a separate concern), see 5.1.
The Flight protocol streams React trees from a server environment to a client. When a Server Component renders a Client Component, it cannot inline the component's implementation — instead it emits an opaque reference identifying where the client should load it. When a client invokes a Server Action, it sends a reference ID that the server resolves back to a function.
The bundler integration layer is responsible for:
'use client' and 'use server' prologue strings.ClientManifest and ServerManifest JSON files that both sides of the Flight protocol use for reference resolution at runtime.React ships multiple react-server-dom-* packages. Each targets a different bundler or runtime environment but shares the same Flight protocol and config abstraction.
| Package | Server bundles | Client bundles | Build tools |
|---|---|---|---|
react-server-dom-webpack | .browser, .node, .edge | .browser, .node, .edge | Webpack plugin, Node ESM loader, Node CJS register |
react-server-dom-turbopack | .browser, .node, .edge | .browser, .node, .edge | — (built into Turbopack) |
react-server-dom-parcel | .browser, .node, .edge | .browser, .node, .edge | — (built into Parcel) |
react-server-dom-esm | .node | .browser, .node | Node ESM loader |
react-server-dom-unbundled | .node | .node | Node ESM loader, Node CJS register |
All server bundles carry the condition: 'react-server' package export condition, ensuring they are only resolved when the bundler or runtime activates that condition.
Sources: scripts/rollup/bundles.js448-814
Both Webpack and Turbopack define the same two reference types. These are the runtime objects that the Flight server inspects during serialization.
Sources: packages/react-server-dom-webpack/src/ReactFlightWebpackReferences.js1-40 packages/react-server-dom-turbopack/src/ReactFlightTurbopackReferences.js1-40
ClientReference<T>{
$$typeof: Symbol.for('react.client.reference') // CLIENT_REFERENCE_TAG
$$id: string // "<moduleId>#<exportName>"
$$async: boolean // true if the chunk is async/lazy
}
ServerReference<T>{
$$typeof: Symbol.for('react.server.reference') // SERVER_REFERENCE_TAG
$$id: string // "<moduleId>#<exportName>"
$$bound: null | Array<ReactClientValue> // closure-captured args via .bind()
$$location: Error // DEV-only source location
}
Reference types and their detection:
Sources: packages/react-server-dom-webpack/src/ReactFlightWebpackReferences.js packages/react-server/src/ReactFlightServer.js78-100
'use client' ModulesWhen the bundler plugin or Node loader encounters a module with 'use client' at the top:
ClientReference stub created by registerClientReference. A module-wide lazy proxy can be created via createClientModuleProxy(moduleId), which returns a Proxy that generates ClientReference objects on property access.'use server' ModulesWhen the bundler plugin or Node loader encounters a module with 'use server':
registerServerReference, giving it a $$typeof, $$id, and $$bound — making it callable directly on the server while also being serializable.Build-time transformation flow:
Sources: packages/react-server-dom-webpack/src/ReactFlightWebpackReferences.js packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js
ReactFlightWebpackPluginReactFlightWebpackPlugin (packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js) is a standard Webpack plugin. It hooks into the compilation lifecycle to:
'use client' or 'use server' directives by inspecting module source.ClientManifest JSON file (for 'use client' modules) and/or a ServerManifest JSON file (for 'use server' modules) into the output directory.The plugin type is RENDERER_UTILS and its output is NODE_ES2015 format, since it runs in the Node.js Webpack process:
entry: 'react-server-dom-webpack/plugin'
externals: ['fs', 'path', 'url', 'neo-async']
Sources: scripts/rollup/bundles.js524-533 packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js
ReactFlightWebpackNodeLoader (ESM Loader)ReactFlightWebpackNodeLoader (packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js) is activated via --experimental-loader react-server-dom-webpack/node-loader when starting the Node.js server process. It:
load hook for each import.acorn to detect 'use client' or 'use server' at the top.'use client' files export ClientReference stubs; 'use server' files wrap exports with registerServerReference.The loader only activates under the react-server condition, i.e., the server rendering process.
Bundle config:
entry: 'react-server-dom-webpack/node-loader'
bundleTypes: [ESM_PROD]
condition: 'react-server'
externals: ['acorn']
Sources: scripts/rollup/bundles.js535-545 packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js
ReactFlightWebpackNodeRegister (CJS Register)ReactFlightWebpackNodeRegister (packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js) is the CommonJS counterpart. It is loaded once at process startup:
It patches Node.js's Module._extensions to intercept .js file loads via require(). The same directive detection and module rewriting logic applies as in the ESM loader.
Bundle config:
entry: 'react-server-dom-webpack/src/ReactFlightWebpackNodeRegister'
bundleTypes: [NODE_ES2015]
condition: 'react-server'
externals: ['url', 'module', 'react-server-dom-webpack/server']
Sources: scripts/rollup/bundles.js547-558 packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js
ClientManifestThe ClientManifest is produced by the plugin at build time and passed to the Flight server at render time:
It is a nested map: { [moduleId]: { [exportName]: ImportManifestEntry } }.
Each ImportManifestEntry value contains:
| Field | Type | Description |
|---|---|---|
id | string | number | Webpack module ID |
chunks | Array<string> | Chunk file IDs that must be loaded before the module |
name | string | Export name, or "*" for namespace import |
async | boolean | Whether the chunk is loaded asynchronously |
When the Flight server serializes a ClientReference, it calls resolveClientReferenceMetadata(bundlerConfig, ref) to extract the ImportManifestEntry, which is then included inline in the Flight wire stream as an import chunk.
Sources: packages/react-server/src/ReactFlightServer.js563-610 packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js
ServerManifest (ServerConsumerModuleMap)The ServerManifest is passed to the Flight client (or to decodeReply for server actions) and used to resolve incoming server reference IDs back to callable functions. On the client side, it is typed as ServerConsumerModuleMap.
Keys are server reference ID strings of the form "<moduleRef>#<exportName>". Values contain:
| Field | Type | Description |
|---|---|---|
id | string | number | Webpack module ID |
name | string | Export name, or "*" for namespace |
chunks | Array<string> | Chunks to load |
When the key does not exist directly, the client splits on # to get the module part and export name separately.
Sources: packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js122-152 packages/react-client/src/ReactFlightClient.js343-371
The Flight server and client packages do not contain bundler-specific code directly. Instead, they import from abstract shim files that are replaced at Rollup build time with bundler-specific implementations, following the same pattern as the reconciler's host config (see 4.6).
| Shim file | Replaced with (Webpack) |
|---|---|
react-server/src/ReactFlightServerConfig.js | ReactFlightClientConfigBundlerWebpack.js (server side) |
react-client/src/ReactFlightClientConfig.js | ReactFlightClientConfigBundlerWebpack.js (client side) |
The fork resolution is driven by scripts/rollup/forks.js and the entry point registry in scripts/shared/inlinedHostConfigs.js.
Config abstraction diagram:
Sources: scripts/rollup/forks.js350-420 scripts/shared/inlinedHostConfigs.js20-44 packages/react-server/src/ReactFlightServer.js78-100 packages/react-client/src/ReactFlightClient.js51-66
The abstract server-side interface includes these functions (consumed by ReactFlightServer.js):
| Function | Purpose |
|---|---|
isClientReference(ref) | Check $$typeof === CLIENT_REFERENCE_TAG |
isServerReference(ref) | Check $$typeof === SERVER_REFERENCE_TAG |
getClientReferenceKey(ref) | Return $$id as the map key |
resolveClientReferenceMetadata(manifest, ref) | Look up ImportManifestEntry in ClientManifest |
getServerReferenceId(ref) | Return $$id |
getServerReferenceBoundArguments(ref) | Return $$bound |
getServerReferenceLocation(ref) | Return $$location (dev-only) |
Sources: packages/react-server/src/ReactFlightServer.js78-100
Reference lifecycle from render to browser:
Sources: packages/react-server/src/ReactFlightServer.js782-812 packages/react-client/src/ReactFlightClient.js885-913
createClientModuleProxycreateClientModuleProxy(moduleId) (packages/react-server-dom-webpack/src/ReactFlightWebpackReferences.js) is used by the Node.js loader and register when synthesizing the replacement module for a 'use client' file. It returns a JavaScript Proxy where:
name on the proxy calls registerClientReference(fn, moduleId, name), which attaches $$typeof = CLIENT_REFERENCE_TAG, $$id = moduleId + '#' + name, and $$async to the function and returns it.This lazy approach allows the loader to avoid static analysis of re-exports and complex module graphs.
Sources: packages/react-server-dom-webpack/src/ReactFlightWebpackReferences.js packages/react-server-dom-turbopack/src/ReactFlightTurbopackReferences.js
'use client')| Stage | Environment | What happens | Key symbol |
|---|---|---|---|
| Build | Webpack plugin | Detects 'use client' in Button.js; writes entry to react-client-manifest.json | ReactFlightWebpackPlugin |
| Server startup | Node.js | require('react-server-dom-webpack/node-register') patches Module._extensions | ReactFlightWebpackNodeRegister |
| Module load | Node.js (RSC) | require('./Button') is intercepted; returns createClientModuleProxy(id) | createClientModuleProxy |
| RSC render | Node.js (RSC) | Server Component accesses Button export; ClientReference object is created | registerClientReference |
| Serialization | Node.js (RSC) | isClientReference(ref) returns true; metadata fetched from manifest | resolveClientReferenceMetadata |
| Stream | Wire | Import chunk I{n}:{...} emitted containing { id, chunks, name } | ReactFlightServer.js |
| Deserialization | Browser | Flight client reads import chunk; calls preloadModule then requireModule | ReactFlightClient.js |
| Render | Browser | Webpack loads the chunk; real Button component renders | __webpack_require__ |
Sources: packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js packages/react-server/src/ReactFlightServer.js packages/react-client/src/ReactFlightClient.js packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js224-270
Refresh this wiki