Package Exports
- vite-plugin-react-server
- vite-plugin-react-server/client
- vite-plugin-react-server/components
- vite-plugin-react-server/config
- vite-plugin-react-server/config/createHandlerOptions
- vite-plugin-react-server/css-loader
- vite-plugin-react-server/dev-server
- vite-plugin-react-server/dev-server/cleanupServerAction
- vite-plugin-react-server/dev-server/configureReactServer
- vite-plugin-react-server/dev-server/configureRequestHandler
- vite-plugin-react-server/dev-server/handleServerAction
- vite-plugin-react-server/dev-server/restartWorker
- vite-plugin-react-server/directives
- vite-plugin-react-server/env
- vite-plugin-react-server/env-loader
- vite-plugin-react-server/env/plugin
- vite-plugin-react-server/error
- vite-plugin-react-server/file-preserver
- vite-plugin-react-server/helpers
- vite-plugin-react-server/helpers/handleServerAction
- vite-plugin-react-server/helpers/resolveStreamElements
- vite-plugin-react-server/html-worker
- vite-plugin-react-server/loader
- vite-plugin-react-server/metrics
- vite-plugin-react-server/orchestrator
- vite-plugin-react-server/orchestrator/createPluginOrchestrator
- vite-plugin-react-server/package.json
- vite-plugin-react-server/plugin
- vite-plugin-react-server/react-client
- vite-plugin-react-server/react-client/plugin
- vite-plugin-react-server/react-server
- vite-plugin-react-server/react-server/plugin
- vite-plugin-react-server/react-static
- vite-plugin-react-server/react-static/createBuildLoader
- vite-plugin-react-server/react-static/plugin
- vite-plugin-react-server/react-static/renderPage
- vite-plugin-react-server/react-static/rscToHtmlStream
- vite-plugin-react-server/react-static/temporaryReferences
- vite-plugin-react-server/register
- vite-plugin-react-server/rsc-worker
- vite-plugin-react-server/server
- vite-plugin-react-server/static
- vite-plugin-react-server/storybook
- vite-plugin-react-server/stream
- vite-plugin-react-server/stream/client
- vite-plugin-react-server/stream/createFromNodeStream
- vite-plugin-react-server/stream/createHtmlStream
- vite-plugin-react-server/stream/createRenderToPipeableStreamHandler
- vite-plugin-react-server/stream/createRscStream
- vite-plugin-react-server/stream/handleRscStream
- vite-plugin-react-server/stream/server
- vite-plugin-react-server/transformer
- vite-plugin-react-server/transformer/plugin
- vite-plugin-react-server/types
- vite-plugin-react-server/utils
- vite-plugin-react-server/utils/rsc-client
- vite-plugin-react-server/vendor
- vite-plugin-react-server/vendor.client
- vite-plugin-react-server/vendor.server
- vite-plugin-react-server/vendor.static
- vite-plugin-react-server/virtual
- vite-plugin-react-server/worker
Readme
vite-plugin-react-server
React Server Components for plain Vite on stable React — no framework, no
experimental builds. One vite build --app prerenders your pages to static
HTML + RSC payloads and emits your components as portable ESM that runs under
any HTTP server: static hosting, Express/Hono, or anything in between.
It works in BOTH Node module conditions, by design: the dev server and the
build run with or without --conditions react-server, and whichever side your
main thread is on, a worker thread mirrors the other half (server components
need a react-server context, client hydration needs a react-client one — you
always have both). Running the main thread under react-server is an optional
optimization — slightly faster, better stack traces — never a requirement.
Install
npm install -D vite-plugin-react-server react react-domvprs 2.0 runs on stable React 19.2+. The react-server-dom-esm transport
ships inside the react-server-loader
dependency (installed automatically), so you no longer install a transport or an
experimental React build yourself. Upgrading from 1.x? See the
migration notes.
Minimal Example
// vite.config.ts
import { defineConfig } from "vite";
import { vitePluginReactServer } from "vite-plugin-react-server";
export default defineConfig({
plugins: vitePluginReactServer({
moduleBase: "src",
Page: "src/page.tsx",
build: { pages: ["/"] },
}),
});// src/page.tsx
export const Page = ({ url }: { url: string }) => <div>Hello from {url}</div>;# Dev server
npx vite
# Build (static site + server/client ESM)
npx vite build --app
# Same build, react-server main thread — optional: a bit faster, better stack traces
NODE_OPTIONS='--conditions react-server' vite build --appBuild Output
dist/
├── static/ # Deployable to any static host
│ ├── index.html # Pre-rendered HTML
│ └── index.rsc # RSC payload for client navigation
├── client/ # Client-side ESM modules (for SSR)
└── server/ # Server-side ESM modules (with server actions)dist/static/ is a complete static site. dist/client/ and dist/server/ are ESM modules you can import in your own Express/Hono/Node server.
Client components
vprs recognises a file as a client module when either of these is true:
- the filename matches
(^|[\/.])client\.[cm]?[jt]sx?$— i.e.Button.client.tsx,bar.client.mjs, or the standalone basenamesrc/client.tsx/client.tsx, or - the file starts with a top-of-file
"use client"directive (leading whitespace, line/block comments, and an optional"use strict"prologue are tolerated above it).
Either is sufficient. Substrings like clientUtils.tsx, clientId.ts, or clients.tsx are not treated as client modules, and a "use client" directive placed after real code does not count.
// src/components/Counter.tsx ← no `.client.` suffix needed
"use client";
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}See Getting Started.
Third-party client-component packages
Component libraries like Chakra UI, MUI, Mantine, react-aria, and framer-motion are client-only — their components rely on React context/state and must run inside a client boundary, the same constraint they carry under Next.js's App Router. Use them within a "use client" component (commonly a small provider wrapper); they can't be imported directly into a server component. This isn't a vprs limitation — e.g. Chakra's own Next.js App Router guide requires wrapping ChakraProvider in a 'use client' component.
vprs auto-detects these so they're treated correctly at build start: any package with react in its peerDependencies is classified as a client package (using vitefu.crawlFrameworkPkgs). Two escape hatches if needed:
vitePluginReactServer({
// Force a package into the list (e.g. one that doesn't peerDep react)
clientPackages: ["@my/internal-ui"],
// Skip a detected one (e.g. devDeps Storybook bringing along @storybook/react)
excludeClientPackages: ["@storybook/react", "@storybook/react-vite"],
});Storybook
vprs ships a Storybook preset — add one line and your RSC app's components build and render in Storybook:
// .storybook/main.ts
export default {
framework: { name: "@storybook/react-vite", options: {} },
addons: ["vite-plugin-react-server/storybook"],
};It strips the vprs plugin from Storybook's builder, resolves the
react-server-dom-esm transport (from react-server-loader), and silences
"use client"/"use server" directive noise. See
Storybook for details. (Requires vprs ≥ 1.9.0.)
Documentation
| Doc | What it covers |
|---|---|
| Getting Started | Install → first page → dev server → build → deploy |
| Storybook | One-line Storybook support for vprs apps |
| Build Output | What the build produces, how to use the ESM modules |
| Configuration | All plugin options |
| CSS Handling | Inline/linked CSS, CSS modules, the Css component |
| Server Actions | "use server" directives, form actions, hosting |
| Examples | Static site, dynamic server, server actions, custom routing |
| Troubleshooting | Common errors and fixes |
| API Reference | Exported functions, types, and components |
Internals (contributors)
| Doc | What it covers |
|---|---|
| Architecture | Condition system, module structure, plugin composition |
| Transformer | How "use client" / "use server" directives are processed |
| Workers | RSC and HTML worker threads |
Maintenance
| Doc | What it covers |
|---|---|
| Releasing | Version bumps, publishing, demo updates |
| React Compatibility | Vendored ESM transport, type system |
Requirements
- Node.js 22.0.0+ (the build uses
node:fs/promises#glob, which landed in 22) - Stable React 19.2+ (
react/react-domat^19.2.7). As of 2.0 the RSC server APIs vprs relies on (prerenderToNodeStream, thereact-servertransport exports) are part of stable React, so no experimental build is required. The matchingreact-server-dom-esmtransport is provided by thereact-server-loaderdependency; experimental React still works if you want the newest RSC features. See React Compatibility. - Vite 6+
TypeScript
{
"compilerOptions": {
"types": ["vite/client", "vite-plugin-react-server/virtual"]
}
}License
MIT