Package Exports
- vite-plugin-react-server
- vite-plugin-react-server/check-react-version
- vite-plugin-react-server/client
- vite-plugin-react-server/components
- vite-plugin-react-server/css-loader
- vite-plugin-react-server/loader
- vite-plugin-react-server/package.json
- vite-plugin-react-server/server
Readme
Vite React Server Plugin
A Vite plugin that enables React Server Components (RSC) streaming and static HTML page generation. It leverages experimental dependencies from React, specifically react-server-dom-esm.
Example Projects
Installation
npm install -D vite-plugin-react-serverOpen Source and Work in Progress
This project uses the latest OSS-experimental React version from the official React GitHub repository. The plugin includes a patch system to facilitate setup. First, install dependencies and patches:
npm install -D patch-package react@experimental react-dom@experimental react-server-dom-esmAdd the following command to your package.json scripts:
"patch": "patch"Run the patch command:
npm run patchIt will instruct you to add:
"postinstall": "patch-package"This ensures the patch is applied after every npm install. If errors arise related to react-server-dom-esm, verify that the postinstall step ran.
Plugin Structure and Purpose
Strict Client-Server Separation
This plugin enforces a strict architectural separation between client and server execution. It achieves this by requiring distinct entry files for both environments, preventing unintended dependencies or cross-thread interactions. While this approach improves maintainability and clarity, it requires additional boilerplate.
The separation is accomplished through two complementary plugins:
- vite-plugin-react-server/client → Handles client-side rendering and ESM bundling
- vite-plugin-react-server → Manages server-side streaming and RSC processing
This ensures that client-side and server-side concerns remain isolated from the beginning, reducing potential inconsistencies.
Custom composition
You can pick and choose only the plugins you like to get the desired behavior as well. For example, we can choose only to use the preserver, the transformer, static plugin, etc.
Worker support
The client plugin uses the rsc-worker to create server side streams. The server plugin uses the html-worker to create client side html. If you don't want to use the rsc-worker, simply don't serve the client plugin. If you don't want to use the html-worker simply don't configure the build.pages option.
Custom Worker
Both workers can be customized using the htmlWorkerPath and rscWorkerPath respectively. The paths will be used to create the workers instead of the prebuilt worker included with this plugin. If these paths are defined, they will be made part of your application build as well.
Keep in mind that, using your custom worker means interacting with the message system of this plugin during development/static generation process.
Plugin Usage
Configuration
import type { StreamPluginOptions } from "vite-plugin-react-server/server";
const createRouter = (file: "props.ts" | "page.tsx") => (url: string) => {
switch (url) {
case "/bidoof":
case "/bidoof/index.rsc":
return `src/page/bidoof/${file}`;
case "/404":
case "/404/index.rsc":
return `src/page/404/${file}`;
case "/":
case "/index.rsc":
return `src/page/${file}`;
default:
throw new Error(`Unknown route: ${url}`);
}
};
export const config = {
moduleBase: "src",
Page: createRouter("page.tsx"),
props: createRouter("props.ts"),
Html: Html,
build: {
pages: ["/", "/bidoof", "/404" ],
},
} satisfies StreamPluginOptions;vite-plugin-react-server/client
Used in vite.config.ts for standard Vite client-side behavior
import { defineConfig, type Plugin } from "vite";
import { vitePluginReactClient } from "vite-plugin-react-server/client";
import { config } from "./vite.react.config";
export default defineConfig({
plugins: vitePluginReactClient(config),
});Build Steps
vite buildOutputs React client-side ESM files to dist/client.
vite build --ssrOutputs files for server-side execution to dist/server.
vite previewServes the static directory.
vite-plugin-react-server
Used in vite.server.config.ts, this plugin strictly separates client and server execution. The client components will be emitted as references.
import { defineConfig, Plugin } from "vite";
import { vitePluginReactServer } from "vite-plugin-react-server";
import { config } from "./vite.react.config";
export default defineConfig({
plugins: vitePluginReactServer(config) as Plugin[],
});Running in Development
NODE_OPTIONS="--conditions=react-server" vite --config vite.server.config.tsBuild Steps
NODE_OPTIONS="--conditions=react-server" vite build --config vite.server.config.tsGenerates server and static folder. The plugin ensures proper SSR handling without requiring --ssr manually.
Note: ssr can still be disabled via config {ssr:false}, which will enable vite's browser virtualization
Static Site Generation
Single-out the static generation step by only inluding the static plugin. Expects client and server folders to be there.
import { defineConfig, Plugin } from "vite";
import { reactStaticPlugin } from "vite-plugin-react-server/static";
import { config } from "./vite.react.config";
export default defineConfig({
plugins: [reactStaticPlugin(config)],
});Example output structure:
dist/static/index.html
dist/static/index.rsc
dist/static/about/index.html
dist/static/about/index.rscThe entire dist/client directory is copied into dist/static, as well as any assets used server-side. Allowing easy deployment by moving the static folder to a hosting service.
Configuration
moduleBase
const config = {
moduleBase: "src",
}Defines the root directory for project modules. This can be customized.
moduleBasePath
moduleBasePath: "",Passed as the second argument to renderToPipeableStream for server-side rendering.
moduleBaseURL
moduleBaseURL: "https://github.com/my-gh-pages",Defines asset URL resolution for CSS collectors and bootstrapModule.
Page and props Mapping
Page: (id) => join(id.replace('index.rsc',''), 'page.tsx')Defines how pages are mapped to file paths.
props: (id) => join(id.replace('index.rsc',''), 'props.ts')Defines how to load the initial props of the page file.
If you do not want prop files, just don't define it.
pageExportName: 'Page',Changes the default name "Page"
propsExportName: 'props',Changes the default name "props"
Example Setup
package.json Scripts
"scripts": {
"build": "build:client && build:server",
"dev": "NODE_OPTIONS='--conditions react-server' vite --config vite.server.config.ts",
"dev:client": "vite",
"build:server": "NODE_OPTIONS='--conditions react-server' vite build --config vite.server.config.ts",
"build:client": "vite build"
}Sample Page Component
// src/my-page.tsx
export const Page = ({ name }) => {
return <div>Hello {name}</div>;
};Sample Props File
// src/my-props.ts
export const props = {
name: "John Doe",
};Vite Configuration Files
Client Configuration (vite.config.ts)
import { vitePluginReactClient } from "vite-plugin-react-server/client";
import { defineConfig } from "vite";
export default defineConfig({
plugins: vitePluginReactClient(),
});Server Configuration (vite.server.config.ts)
import { vitePluginReactServer } from "vite-plugin-react-server";
import { defineConfig } from "vite";
export default defineConfig({
plugins: vitePluginReactServer(),
});NODE_OPTIONS='--conditions react-server' npx vite --config vite.server.config.tsIn development mode, the server plugin pipes the React stream directly to the response.
NODE_OPTIONS='--conditions react-server' npx vite build --config vite.server.config.tsThis builds the dist/server directory. It sets ssr to true by default, so you can't forget to. Additionally, when the build is done it generates the dist/static directory using the /static plugin.
Contributions
If you want to help develop or maintain the plugin feel free to open a PR or issue on GitHub.