Package Exports
- react-server-loader
- react-server-loader/client
- react-server-loader/client.browser
- react-server-loader/client.node
- react-server-loader/directives
- react-server-loader/loader
- react-server-loader/package.json
- react-server-loader/server
- react-server-loader/server.node
- react-server-loader/static
- react-server-loader/static.node
- react-server-loader/transformer
Readme
react-server-loader
Loader-side tooling for React Server Components in ESM environments.
Scope: use React, but with a native-ESM workflow in mind. Anything that helps you run React (and RSC) in pure ESM belongs here; shipping a copy of React does not —
react/react-domalways come from the consumer.
React doesn't publish an ESM RSC transport to npm. This package fills that gap with the four things a bundler, framework, or canonical Node setup needs to render React Server Components in pure ESM:
- A vendored
react-server-dom-esmtransport — the RSC wire format React builds but doesn't ship to npm, vendored so you don't build it from source. - A directive engine that decides whether a module declares
"use client"or"use server"at the top level — without false positives on identifiers or strings that merely contain those words. - Transformer primitives that rewrite a directive-bearing module into
the
registerClientReference/registerServerReferenceshape the transport expects at runtime. - A Node ESM loader factory (
createReactLoader) that wires the above intonode:module#register, so a plainnode --import … --conditions react-serverserves RSC modules with no bundler at all.
Install
react-server-loader ships two trains, one per React release channel.
Its version equals the react-server-dom-esm (React) version it vendors, and
the transport is hard-bound to that React's internals — so install a
matching react / react-dom. Pick the train for the React you build
against; the peer-dependency check flags any skew.
Stable React 19 (the latest tag):
npm install react-server-loader react react-domExperimental React (the experimental tag — newest RSC features):
npm install react-server-loader@experimental react@experimental react-dom@experimentalEither way you need a React 19+ build with React Server Components
support. The experimental train pins react / react-dom to an exact build
string and needs more care — see Versioning for the
full rationale and the exact-pin recipe.
Quickstart: render RSC with no bundler
Two steps — register the loader so directive modules transform on import,
then render and decode with the transport. The server half runs under
node --conditions react-server so react and the transport resolve to
their server builds.
// register.mjs — install the loader
import { register } from "node:module";
import { createReactLoader } from "react-server-loader/loader";
const { load } = createReactLoader({
// Maps an on-disk module to the id baked into each emitted
// registerClientReference — i.e. where the client will fetch it from.
moduleID: (filePath) => filePath.replace(process.cwd(), ""),
});
register(load, import.meta.url);// server.tsx — render a server component tree to an RSC stream
import { renderToPipeableStream } from "react-server-loader/server";
import App from "./App.js";
const { pipe } = renderToPipeableStream(<App />, "/");
pipe(destination); // any Node Writable — receives the RSC payload// client.tsx — decode the stream back into a React tree
import { createFromNodeStream } from "react-server-loader/client";
const root = await createFromNodeStream(rscStream, "/", "/");node --import ./register.mjs --conditions=react-server server.jsThe full walkthrough — server component, directive modules, and why each path argument lines up — is in Getting started.
Subpaths
| Subpath | Surface |
|---|---|
react-server-loader/loader |
createReactLoader → Node ESM load / resolve hooks; the Logger contract + CONSOLE_LOGGER / NULL_LOGGER backends. |
react-server-loader/directives |
Directive engine: detectClientModule, sourceHasTopLevelClientDirective, analyzeModule. Pure analysis, no transport dependency. |
react-server-loader/transformer |
Source-to-source transform: createTransformer, parse, transformModule. |
react-server-loader/server (/server.node) |
Vendored transport, server: renderToPipeableStream, registerClientReference, registerServerReference, decodeReply, createTemporaryReferenceSet (needs --conditions react-server). |
react-server-loader/client (/client.node, /client.browser) |
Vendored transport, client: createFromNodeStream, createServerReference. |
react-server-loader/static (/static.node) |
Vendored transport, static entry. In this React build it re-exports the server surface — react-server-dom-esm ships no separate static module. |
react-server-loader |
Re-exports the full public surface for convenience (headline: createReactLoader, detectClientModule, createTransformer). |
Import only from these subpaths and the symbols named here — they are the supported, consumer-facing surface. AST type-guards and low-level transform helpers are internal, not exported, and may change without a major bump. Full per-subpath signatures are in the API reference.
Documentation
- Getting started — install the two trains and run the end-to-end no-bundler RSC example.
- Integrating into a bundler or framework — the
transformhook and the server loader, with Vite as the worked example. - API reference — per-subpath signatures for the consumer-facing surface.
- Versioning — the two trains, the peer-dependency
contract, and why
react/react-dommust match. - Troubleshooting — symptom → cause → fix for
version skew, the
react-servercondition, and peer-dep errors.
Implementing or releasing rsl? See docs/internals/ for the architecture, the directive engine, the transformer primitives, and the vendoring/publishing pipeline.
License
MIT