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/metrics
- vite-plugin-react-server/package.json
- vite-plugin-react-server/server
- vite-plugin-react-server/types
- vite-plugin-react-server/utils
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
Environment-Based Execution
This plugin uses environment detection to determine the execution context. It achieves this by checking the NODE_OPTIONS environment variable:
import { getCondition } from "vite-plugin-react-server"
if(getCondition() !== 'react-server'){
throw new Error('-10 poision damage')
}Alternatively, you can pass the argument for the react- prefix to just get client or server back.
import { getCondition } from "vite-plugin-react-server"
import(`plugin.${getCondition('')}.js`)The main entry point adapts based on the environment:
- Client Mode (default) → Does not require the react-server condition, uses a worker thread for RSC requests
- Server Mode (
NODE_OPTIONS="--conditions react-server") → Does not need worker thread for RSC requests
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 plugin without the react-server condition. 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.
For more information on creating your custom workers, see docs
Plugin Usage
import { defineConfig, type Plugin } from "vite";
import { vitePluginReactClient } from "vite-plugin-react-server";
import { config } from "./vite.react.config";
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;
export default defineConfig({
plugins: vitePluginReactClient(config),
});Built-in React Server Components
This plugin has two built-in React Component, each can be configured through the options to be your own component. Defining your custom React server components will affect the final production output, they won't be used during development.
- Html - used as the wrapper for production pages (use vite's
index.htmlfor the development wrapper and entry point for client files) - CssCollector - used to emit
<link>and<style>tags based oncssconfig
Build Steps
vite buildTargets browsers, outputs to dist/static.
vite build --ssrTargets non-react-server node environment, used for server-side-rendering, outputs to dist/client.
NODE_OPTIONS="--conditions=react-server" vite buildTargets react-server-only environment, outputs to dist/server. In this case, ssr is implied and defaults to true.
vite-plugin-react-server
import { defineConfig, Plugin } from "vite";
import { vitePluginReactServer } from "vite-plugin-react-server";
import { config } from "./vite.react.config";
export default defineConfig({
plugins: vitePluginReactServer(config),
});Running in Development
NODE_OPTIONS="--conditions=react-server" viteIs the recommended way for a more direct server pipeline that doesn't require a rsc-worker.
To develop the app using the rsc-worker, simply run
vitewithout the react-server condition.
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.rscThis plugin is included by default when the react-server condition is set.
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:static && build:client && build:server",
"dev": "NODE_OPTIONS='--conditions react-server' vite",
"start": "vite",
"build:server": "NODE_OPTIONS='--conditions react-server' vite build",
"build:client": "vite build --ssr",
"build:static": "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",
};Contributions
If you want to help develop or maintain the plugin feel free to open a PR or issue on GitHub.