Skip to content

nicobrinkkemper/vite-plugin-react-server

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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-server

Open 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-esm

Add the following command to your package.json scripts:

"patch": "patch"

Run the patch command:

npm run patch

It 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 build

Outputs React client-side ESM files to dist/client.

vite build --ssr

Outputs files for server-side execution to dist/server.

vite preview

Serves 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.ts

Build Steps

NODE_OPTIONS="--conditions=react-server" vite build --config vite.server.config.ts

Generates 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.rsc

The 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.ts

In 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.ts

This 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.

About

Vite plugin for React Server Components (RSC)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published