vite-plugin-react-server
Version:
Vite plugin for React Server Components (RSC)
166 lines (126 loc) • 6.69 kB
Markdown
# vite-plugin-react-server
A Vite plugin that transforms React components into native ESM modules with React Server Components support. Build static sites, dynamic servers, or anything in between — your components become portable ESM that works with any HTTP server.
## Install
```bash
npm install -D vite-plugin-react-server react react-dom
```
vprs 2.0 runs on **stable React 19.2+**. The `react-server-dom-esm` transport
ships inside the [`react-server-loader`](https://www.npmjs.com/package/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](./docs/getting-started.md#upgrading-from-1x).
## Minimal Example
```ts
// 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: ["/"] },
}),
});
```
```tsx
// src/page.tsx
export const Page = ({ url }: { url: string }) => <div>Hello from {url}</div>;
```
```bash
# Dev server
npx vite
# Build
NODE_OPTIONS='--conditions react-server' vite build --app
```
## Build 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 basename `src/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.
```tsx
// 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](./docs/getting-started.md#the-client-filename-is-optional).
## 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](https://v2.chakra-ui.com/getting-started/nextjs-app-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`](https://github.com/svitejs/vitefu)). Two escape hatches if needed:
```ts
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:
```ts
// .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](./docs/storybook.md) for details. (Requires vprs ≥ 1.9.0.)
## Documentation
| Doc | What it covers |
|-----|---------------|
| [Getting Started](./docs/getting-started.md) | Install → first page → dev server → build → deploy |
| [Storybook](./docs/storybook.md) | One-line Storybook support for vprs apps |
| [Build Output](./docs/build-output.md) | What the build produces, how to use the ESM modules |
| [Configuration](./docs/configuration.md) | All plugin options |
| [CSS Handling](./docs/css-handling.md) | Inline/linked CSS, CSS modules, the `Css` component |
| [Server Actions](./docs/server-actions.md) | `"use server"` directives, form actions, hosting |
| [Examples](./docs/examples.md) | Static site, dynamic server, server actions, custom routing |
| [Troubleshooting](./docs/troubleshooting.md) | Common errors and fixes |
| [API Reference](./docs/api-reference.md) | Exported functions, types, and components |
### Internals (contributors)
| Doc | What it covers |
|-----|---------------|
| [Architecture](./docs/internals/architecture.md) | Condition system, module structure, plugin composition |
| [Transformer](./docs/internals/transformer.md) | How `"use client"` / `"use server"` directives are processed |
| [Workers](./docs/internals/workers.md) | RSC and HTML worker threads |
### Maintenance
| Doc | What it covers |
|-----|---------------|
| [Releasing](./docs/releasing.md) | Version bumps, publishing, demo updates |
| [React Compatibility](./docs/react-type-compatibility.md) | 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-dom` at `^19.2.7`). As of 2.0 the RSC
server APIs vprs relies on (`prerenderToNodeStream`, the `react-server`
transport exports) are part of stable React, so no experimental build is
required. The matching `react-server-dom-esm` transport is provided by the
`react-server-loader` dependency; experimental React still works if you want
the newest RSC features. See [React Compatibility](./docs/react-type-compatibility.md).
- Vite 6+
## TypeScript
```json
{
"compilerOptions": {
"types": ["vite/client", "vite-plugin-react-server/virtual"]
}
}
```
## License
MIT