React Compatibility
Supported Versions
As of 2.0, the plugin runs on stable React 19.2+. The RSC server APIs it
depends on (prerenderToNodeStream and the react-server transport exports)
graduated out of the experimental channel, so an experimental build is no longer
required. The react-server-dom-esm transport is provided by the
react-server-loader
dependency, whose peer range pins the exact React build the transport was vendored
against.
| React Version | Support | Notes |
|---|---|---|
| React 19.2+ stable | ✅ Supported | The default. react-server-loader's stable train vendors a transport built against this line; install plain react / react-dom and the peer ranges line up. |
react@experimental (any 0.0.0-experimental-* prerelease) | ✅ Supported | Still works for the newest RSC features. Use react-server-loader@experimental, which pins the exact experimental React it was built against. |
| React 19.0 / 19.1 stable | ⚠️ Untested | Predates the prerender/transport graduation vprs 2.0 targets; upgrade to 19.2+. |
| React 18 stable | ❌ Not supported | Missing RSC APIs |
npm install react@^19.2.7 react-dom@^19.2.7
For the experimental train, pin the exact React react-server-loader@experimental
was built against (the @experimental dist-tag moves daily):
npm view react-server-loader@experimental peerDependencies
npm install react@<that-exact-version> react-dom@<that-exact-version>
react-server-loader is a regular dependency whose range admits both
trains (^19.2.10 || >=0.0.0-0 <0.0.1), so every package manager installs it
for you — the stable train by default, no extra step (yarn included).
To run the experimental train, install all three React packages at the
@experimental tag. The loader's range also admits experimental, so npm
collapses to a single copy alongside your experimental React — no overrides,
no duplicate in the tree:
npm install react@experimental react-dom@experimental react-server-loader@experimental
One practical reason to run experimental today: stable React 19.2.x emits its
CSS preload hint as an invalid as="stylesheet", so browsers ignore the
preload — styles still load, just not preloaded (see
troubleshooting). The experimental channel already
carries the fix, and it reaches stable when React ships it.
React peer: react / react-dom at ^19.2.7 || >=0.0.0-0 <0.0.1 (admits
both trains). The transport binds to a single React build's internals and
throws on a mismatch, so keep react, react-dom, and react-server-loader
on the same train. See
react-server-loader's versioning
for why the versions line up the way they do.
History. vprs 1.x required
react@experimentaland bundled the transport in-repo undeross-experimental/, because the RSC server internals it read only existed on the experimental channel at the time. Those APIs have since shipped in stable React, and 2.0 moved the transport out toreact-server-loader.
Looking ahead: react-server-dom-esm on npm
react-server-loader vendors the react-server-dom-esm transport because, to
date, that package has never been published to npm — installing it directly
gives an empty 0.0.1 placeholder, so downstream tools have had to build and
bundle it from React source for each release.
There's an open proposal to change that:
react/react#36768 would publish
react-server-dom-esm to npm. It's still under review and not guaranteed to
land. If it ships and a future vprs release adopts it, the transport could be
installed straight from React's own published package and react-server-loader
would no longer be the dependency that carries it. Nothing changes until then —
react-server-loader is installed for you as described above. Follow the PR if
you want to track where this goes.
ESM Transport
The plugin consumes a vendored build of react-server-dom-esm from the
react-server-loader dependency — no separate install or patching needed.
How It Works
- The transport ships inside
react-server-loader(node_modules/react-server-loader/vendor/react-server-dom-esm/); a singletransportDirhelper resolves it via the package. - A Vite alias plugin resolves all
react-server-dom-esm/*imports to that copy. - In dev mode, a
node_modules/react-server-dom-esmsymlink is auto-created (viaconfigResolved) so Vite's module runner and the RSC worker resolve the bare specifier natively. - Server-side entries are marked external during builds and resolved at runtime via
createRequire.
Runtime Usage Outside Vite
If you use plugin utilities outside of Vite (startup scripts, SSR servers), register the resolver:
node --import vite-plugin-react-server/register ./your-script.mjs
Updating the Transport
The transport is built and vendored by react-server-loader. To move to a newer React, bump the react-server-loader dependency (and the matching react / react-dom) — vprs no longer builds the transport itself.
Type System
The plugin exports generic types that adapt to your React version. For most usage, the defaults work:
import type { HtmlProps, RootProps, PageComponentType } from "vite-plugin-react-server/types";
import { Css } from "vite-plugin-react-server/components";
export const Html = ({ Root, cssFiles, globalCss, pageProps, Page }: HtmlProps) => (
<html>
<head>
<Css cssFiles={globalCss} />
</head>
<body>
<Root cssFiles={cssFiles} Page={Page} pageProps={pageProps} />
</body>
</html>
);
For custom type constraints, the generics accept PageProps, As, InlineCSS, and ReactType parameters. See plugin/types.ts for full signatures, or the simplified versions in API Reference.
Type Declarations
Add to your tsconfig.json for virtual module types:
{
"compilerOptions": {
"types": ["vite-plugin-react-server/virtual"]
}
}
This provides types for virtual:react-server/hmr, import.meta.env.PUBLIC_ORIGIN, and other virtual modules.