# 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
