From 470b81ad270df538130414bba6012308bcc4da8f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Date: Mon, 27 Jul 2026 18:55:45 +0900 Subject: [PATCH] docs(rsc): improve README prose Co-authored-by: OpenCode --- packages/plugin-rsc/README.md | 311 ++++++++++++++++------------------ 1 file changed, 143 insertions(+), 168 deletions(-) diff --git a/packages/plugin-rsc/README.md b/packages/plugin-rsc/README.md index e2d11b867..a4cdda01a 100644 --- a/packages/plugin-rsc/README.md +++ b/packages/plugin-rsc/README.md @@ -4,14 +4,14 @@ This package provides [React Server Components](https://react.dev/reference/rsc/ ## Features -- **Framework-agnostic**: The plugin implements [RSC bundler features](https://react.dev/reference/rsc/server-components) and provides low level RSC runtime (`react-server-dom`) API without framework-specific abstractions. -- **Runtime-agnostic**: Built on [Vite environment API](https://vite.dev/guide/api-environment.html) and works with other runtimes (e.g., [`@cloudflare/vite-plugin`](https://github.com/cloudflare/workers-sdk/tree/main/packages/vite-plugin-cloudflare)). -- **HMR support**: Enables editing both client and server components without full page reloads. -- **CSS support**: CSS is automatically code-split both at client and server components and they are injected upon rendering. +- **Framework-agnostic**: The plugin implements [RSC bundler features](https://react.dev/reference/rsc/server-components) and provides low-level RSC runtime (`react-server-dom`) APIs without framework-specific abstractions. +- **Runtime-agnostic**: The plugin builds on the [Vite Environment API](https://vite.dev/guide/api-environment.html) and supports other runtimes, such as [`@cloudflare/vite-plugin`](https://github.com/cloudflare/workers-sdk/tree/main/packages/vite-plugin-cloudflare). +- **HMR support**: Edit Client and Server Components without a full-page reload. +- **CSS support**: CSS is automatically code-split for Client and Server Components, then injected when the components render. ## Getting Started -You can create a starter project by: +Create a starter project with the following command: ```sh npm create vite@latest -- --template rsc @@ -19,48 +19,47 @@ npm create vite@latest -- --template rsc ## Examples -**Start here:** [`./examples/starter`](./examples/starter) - Recommended for understanding the package +**Start here:** [`./examples/starter`](./examples/starter) -- Provides an in-depth overview of API with inline comments to explain how they function within RSC-powered React application. +This example provides an in-depth API overview, with inline comments that explain how the APIs work in an RSC-powered React application. **Integration examples:** -- [`./examples/basic`](./examples/basic) - Comprehensive showcase of standard RSC features and the primary E2E test fixture. -- [`./examples/use-cache`](./examples/use-cache) - Minimal cache feature inspired by Next.js's `"use cache"`, built with generic transform and RSC runtime APIs. -- [`./examples/custom-server-function`](./examples/custom-server-function) - Third-party Server Function directive integration using server reference claims. -- [`./examples/ssg`](./examples/ssg) - Static site generation with MDX and client components for interactivity. -- [`./examples/ppr`](./examples/ppr) - Partial prerendering with a reusable static HTML shell and request-time RSC content. -- [`./examples/no-ssr`](./examples/no-ssr) - RSC application without an SSR environment. -- [`./examples/client-first`](./examples/client-first) - Experimental client-owned page that consumes RSC function results. -- [`./examples/browser-mode`](./examples/browser-mode) - Advanced setup that runs both RSC and React client environments in the browser with custom module loading. -- [`./examples/performance-track`](./examples/performance-track) - Minimal React Server Components performance track probe. -- [`./examples/react-router`](./examples/react-router) - React Router RSC integration - - Demonstrates how to integrate [experimental React Router RSC API](https://remix.run/blog/rsc-preview). React Router now provides [official RSC support](https://reactrouter.com/how-to/react-server-components), so it's recommended to follow React Router's official documentation for the latest integration. +- [`./examples/basic`](./examples/basic): A comprehensive showcase of standard RSC features and the primary E2E test fixture. +- [`./examples/use-cache`](./examples/use-cache): A minimal cache feature inspired by Next.js's `"use cache"`, built with generic transform APIs and RSC runtime APIs. +- [`./examples/custom-server-function`](./examples/custom-server-function): An integration of third-party Server Function directives using server reference claims. +- [`./examples/ssg`](./examples/ssg): Static site generation with MDX and interactive Client Components. +- [`./examples/ppr`](./examples/ppr): Partial prerendering with a reusable static HTML shell and request-time RSC content. +- [`./examples/no-ssr`](./examples/no-ssr): An RSC application without an SSR environment. +- [`./examples/client-first`](./examples/client-first): An experimental client-owned page that consumes RSC function results. +- [`./examples/browser-mode`](./examples/browser-mode): An advanced setup that runs both the RSC and React client environments in the browser with custom module loading. +- [`./examples/performance-track`](./examples/performance-track): A minimal probe for React Server Components performance tracks. +- [`./examples/react-router`](./examples/react-router): An integration with the [experimental React Router RSC API](https://remix.run/blog/rsc-preview). React Router now provides [official RSC support](https://reactrouter.com/how-to/react-server-components), so refer to its official documentation for the latest integration guidance. ## Basic Concepts -This example is a simplified version of [`./examples/starter`](./examples/starter). You can read [`./examples/starter/src/framework/entry.{rsc,ssr,browser}.tsx`](./examples/starter/src/framework) for more in-depth commentary, which includes server function handling and client-side RSC re-fetching/re-rendering. +This example is a simplified version of [`./examples/starter`](./examples/starter). For more detailed commentary, including Server Function handling and client-side RSC refetching and rerendering, read [`./examples/starter/src/framework/entry.{rsc,ssr,browser}.tsx`](./examples/starter/src/framework). -This is the diagram to show the basic flow of RSC rendering process. See also https://github.com/hi-ogawa/vite-plugins/discussions/606. +The following diagram shows the basic RSC rendering flow. For more context, see [this discussion](https://github.com/hi-ogawa/vite-plugins/discussions/606). ```mermaid graph TD subgraph "rsc environment" - A["React virtual dom tree"] --> |"[@vitejs/plugin-rsc/rsc]
renderToReadableStream"| B1["RSC Stream"]; + A["React virtual DOM tree"] --> |"[@vitejs/plugin-rsc/rsc]
renderToReadableStream"| B1["RSC stream"]; end B1 --> B2 B1 --> B3 subgraph "ssr environment" - B2["RSC Stream"] --> |"[@vitejs/plugin-rsc/ssr]
createFromReadableStream"| C1["React virtual dom tree"]; - C1 --> |"[react-dom/server]
SSR"| E["HTML String/Stream"]; + B2["RSC stream"] --> |"[@vitejs/plugin-rsc/ssr]
createFromReadableStream"| C1["React virtual DOM tree"]; + C1 --> |"[react-dom/server]
SSR"| E["HTML string/stream"]; end subgraph "client environment" - B3["RSC Stream"] --> |"[@vitejs/plugin-rsc/browser]
createFromReadableStream"| C2["React virtual dom tree"]; - C2 --> |"[react-dom/client]
CSR: mount, hydration"| D["DOM Elements"]; + B3["RSC stream"] --> |"[@vitejs/plugin-rsc/browser]
createFromReadableStream"| C2["React virtual DOM tree"]; + C2 --> |"[react-dom/client]
CSR: mount, hydration"| D["DOM elements"]; end style A fill:#D6EAF8,stroke:#333,stroke-width:2px @@ -81,16 +80,16 @@ import { defineConfig } from 'vite' export default defineConfig({ plugins: [ - // add plugin + // Add the plugin. rsc(), ], - // specify entry point for each environment. + // Specify an entry point for each environment. environments: { - // `rsc` environment loads modules with `react-server` condition. - // this environment is responsible for: + // The `rsc` environment loads modules with the `react-server` condition. + // This environment is responsible for: // - RSC stream serialization (React VDOM -> RSC stream) - // - server functions handling + // - handling Server Functions rsc: { build: { rollupOptions: { @@ -101,8 +100,8 @@ export default defineConfig({ }, }, - // `ssr` environment loads modules without `react-server` condition. - // this environment is responsible for: + // The `ssr` environment loads modules without the `react-server` condition. + // This environment is responsible for: // - RSC stream deserialization (RSC stream -> React VDOM) // - traditional SSR (React VDOM -> HTML string/stream) ssr: { @@ -115,12 +114,12 @@ export default defineConfig({ }, }, - // client environment is used for hydration and client-side rendering - // this environment is responsible for: + // The client environment handles hydration and client-side rendering. + // This environment is responsible for: // - RSC stream deserialization (RSC stream -> React VDOM) - // - traditional CSR (React VDOM -> Browser DOM tree mount/hydration) - // - refetch and re-render RSC - // - calling server functions + // - traditional CSR (React VDOM -> browser DOM tree mount/hydration) + // - refetching and rerendering RSC + // - calling Server Functions client: { build: { rollupOptions: { @@ -139,9 +138,9 @@ export default defineConfig({ ```tsx import { renderToReadableStream } from '@vitejs/plugin-rsc/rsc' -// the plugin assumes `rsc` entry having default export of request handler +// The plugin expects the `rsc` entry's default export to be a request handler. export default async function handler(request: Request): Promise { - // serialize React VDOM to RSC stream + // Serialize the React VDOM to an RSC stream. const root = ( @@ -151,7 +150,7 @@ export default async function handler(request: Request): Promise { ) const rscStream = renderToReadableStream(root) - // respond direct RSC stream request based on framework's convention + // Respond to a direct RSC stream request according to the framework's convention. if (request.url.endsWith('.rsc')) { return new Response(rscStream, { headers: { @@ -160,14 +159,14 @@ export default async function handler(request: Request): Promise { }) } - // delegate to SSR environment for html rendering - // `loadModule` is a helper API provided by the plugin for multi environment interaction. + // Delegate HTML rendering to the SSR environment. + // `loadModule` lets multiple environments interact. const ssrEntry = await import.meta.viteRsc.loadModule< typeof import('./entry.ssr.tsx') >('ssr', 'index') const htmlStream = await ssrEntry.handleSsr(rscStream) - // respond html + // Respond with HTML. return new Response(htmlStream, { headers: { 'Content-type': 'text/html', @@ -175,7 +174,7 @@ export default async function handler(request: Request): Promise { }) } -// add `import.meta.hot.accept` to handle server module change efficiently +// Accept server module changes without reloading the page. if (import.meta.hot) { import.meta.hot.accept() } @@ -188,14 +187,14 @@ import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' import { renderToReadableStream } from 'react-dom/server.edge' export async function handleSsr(rscStream: ReadableStream) { - // deserialize RSC stream back to React VDOM + // Deserialize the RSC stream back into a React VDOM. const root = await createFromReadableStream(rscStream) - // helper API to allow referencing browser entry content from SSR environment + // Reference browser entry content from the SSR environment. const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent('index') - // render html (traditional SSR) + // Render HTML with traditional SSR. const htmlStream = renderToReadableStream(root, { bootstrapScriptContent, }) @@ -211,36 +210,36 @@ import { createFromReadableStream } from '@vitejs/plugin-rsc/browser' import { hydrateRoot } from 'react-dom/client' async function main() { - // fetch and deserialize RSC stream back to React VDOM + // Fetch and deserialize the RSC stream back into a React VDOM. const rscResponse = await fetch(window.location.href + '.rsc') const root = await createFromReadableStream(rscResponse.body) - // hydration (traditional CSR) + // Hydrate the server-rendered HTML. hydrateRoot(document, root) } main() ``` -## Environment helper API +## Environment Helper API -The plugin provides an additional helper for multi environment interaction. +The plugin provides helper APIs for interactions between environments. -### Available on `rsc` or `ssr` environment +### Available in the `rsc` and `ssr` environments #### `import.meta.viteRsc.loadModule` - Type: `(environmentName: "ssr" | "rsc", entryName?: string) => Promise` -This allows importing `ssr` environment module specified by `environments.ssr.build.rollupOptions.input[entryName]` inside `rsc` environment and vice versa. When `entryName` is omitted, the function automatically uses the single entry from the target environment's `rollupOptions.input`. +This API imports an entry from the `ssr` environment into the `rsc` environment, or vice versa. For example, the `ssr` entry is specified by `environments.ssr.build.rollupOptions.input[entryName]`. When you omit `entryName`, the API uses the single entry from the target environment's `rollupOptions.input`. -During development, by default, this API assumes both `rsc` and `ssr` environments execute under the main Vite process as `RunnableDevEnvironment`. Internally, `loadModule` uses the global `__VITE_ENVIRONMENT_RUNNER_IMPORT__` function to import modules in the target environment (see [`__VITE_ENVIRONMENT_RUNNER_IMPORT__`](#__vite_environment_runner_import__) below). +By default, this API assumes during development that the `rsc` and `ssr` environments run as `RunnableDevEnvironment` instances in the main Vite process. Internally, `loadModule` uses the global [`__VITE_ENVIRONMENT_RUNNER_IMPORT__`](#__vite_environment_runner_import__) function to import modules into the target environment. -When enabling `rsc({ loadModuleDevProxy: true })` plugin option, the loaded module is implemented as a proxy with `fetch`-based RPC to call in node environment on the main Vite process, which for example, allows `rsc` environment inside cloudflare workers to access `ssr` environment on the main Vite process. This proxy mechanism uses [turbo-stream](https://github.com/jacob-ebey/turbo-stream) for serializing data types beyond JSON, with custom encoders/decoders to additionally support `Request` and `Response` instances. +When you enable the `rsc({ loadModuleDevProxy: true })` option, the plugin represents the loaded module as a proxy that uses `fetch`-based RPC to make calls in the Node.js environment of the main Vite process. For example, this proxy allows an `rsc` environment running in Cloudflare Workers to access an `ssr` environment in the main Vite process. The proxy uses [turbo-stream](https://github.com/jacob-ebey/turbo-stream) to serialize data types beyond those supported by JSON, with custom encoders and decoders for `Request` and `Response` instances. -During production build, this API will be rewritten into a static import of the specified entry of other environment build and the modules are executed inside the same runtime. +During a production build, the plugin rewrites this API as a static import of the specified entry from the other environment's build. The modules then run in the same runtime. -For example, +For example: ```js // ./entry.rsc.tsx @@ -255,47 +254,47 @@ export function renderHTML(...) {} - Type: `(specifier: string, options: { environment: string }) => Promise` -A more ergonomic alternative to `loadModule`: +This API is a more ergonomic alternative to `loadModule` because: -1. No manual `rollupOptions.input` config needed - entries are auto-discovered -2. Specifier matches the path in `typeof import(...)` type annotations +1. It automatically discovers entries, so you do not need to configure `rollupOptions.input` manually. +2. Its specifier matches the path in the `typeof import(...)` type annotation. **Comparison:** ```ts -// Before (loadModule) - requires vite.config.ts: +// Before: loadModule requires vite.config.ts configuration. // environments.ssr.build.rollupOptions.input = { index: './entry.ssr.tsx' } import.meta.viteRsc.loadModule('ssr', 'index') -// After (import) - no config needed, auto-discovered +// After: import discovers the entry without configuration. import.meta.viteRsc.import( './entry.ssr.tsx', { environment: 'ssr' }, ) ``` -During development, this works the same as `loadModule`, using the `__VITE_ENVIRONMENT_RUNNER_IMPORT__` function to import modules in the target environment. +During development, this API works like `loadModule`: it uses the `__VITE_ENVIRONMENT_RUNNER_IMPORT__` function to import modules into the target environment. -During production build, the plugin auto-discovers these imports and emits them as entries in the target environment. A manifest file (`__vite_rsc_env_imports_manifest.js`) is generated to map module specifiers to their output filenames. +During a production build, the plugin discovers these imports and emits them as entries in the target environment. It also generates a manifest file (`__vite_rsc_env_imports_manifest.js`) that maps module specifiers to their output filenames. -### Available on `rsc` environment +### Available in the `rsc` environment #### `import.meta.viteRsc.loadCss` > [!NOTE] -> The plugin automatically injects CSS for server components. See the [CSS Support](#css-support) section for detailed information about automatic CSS injection. +> The plugin automatically injects CSS for Server Components. See the [CSS Support](#css-support) section for detailed information about automatic CSS injection. - Type: `(importer?: string) => React.ReactNode` -This allows collecting css which is imported through a current server module and injecting them inside server components. +This API collects CSS imported directly or transitively by the current server module and injects it into Server Components. ```tsx import './test.css' import dep from './dep.tsx' export function ServerPage() { - // this will include css assets for "test.css" - // and any css transitively imported through "dep.tsx" + // Include CSS assets for "test.css" and any CSS imported transitively + // through "dep.tsx". return ( <> {import.meta.viteRsc.loadCss()} @@ -305,7 +304,7 @@ export function ServerPage() { } ``` -When specifying `loadCss()`, it will collect css through the server module resolved by ``. +When you specify `loadCss()`, the API collects CSS through the server module resolved from ``. ```tsx // virtual:my-framework-helper @@ -330,11 +329,11 @@ export function UserApp() { } ``` -### Available on `ssr` environment +### Available in the `ssr` environment #### `import.meta.viteRsc.loadBootstrapScriptContent("index")` -This provides a raw js code to execute a browser entry file specified by `environments.client.build.rollupOptions.input.index`. This is intended to be used with React DOM SSR API, such as [`renderToReadableStream`](https://react.dev/reference/react-dom/server/renderToReadableStream) +This API returns the raw JavaScript needed to execute the browser entry specified by `environments.client.build.rollupOptions.input.index`. Use it with a React DOM SSR API such as [`renderToReadableStream`](https://react.dev/reference/react-dom/server/renderToReadableStream). ```js import { renderToReadableStream } from 'react-dom/server.edge' @@ -346,19 +345,19 @@ const htmlStream = await renderToReadableStream(reactNode, { }) ``` -### Available on `client` environment +### Available in the `client` environment #### `rsc:update` event -This event is fired when server modules are updated, which can be used to trigger re-fetching and re-rendering of RSC components on browser. +This event fires when server modules are updated. Use it to trigger the refetching and rerendering of RSC components in the browser. ```js import { createFromFetch } from '@vitejs/plugin-rsc/browser' import.meta.hot.on('rsc:update', async () => { - // re-fetch RSC stream + // Refetch the RSC stream. const rscPayload = await createFromFetch(fetch(window.location.href + '.rsc')) - // re-render ... + // Rerender the components. }) ``` @@ -368,9 +367,9 @@ import.meta.hot.on('rsc:update', async () => { - Type: `(environmentName: string, id: string) => Promise` -This global function provides a standardized way to import a module in a given environment during development. It is used internally by `import.meta.viteRsc.loadModule` to execute modules in the target environment. +During development, this global function provides a standard way to import a module into a specified environment. `import.meta.viteRsc.loadModule` uses it internally to execute modules in the target environment. -By default, the plugin sets this global to import via the environment's module runner: +By default, the plugin configures this global to import modules through the environment's module runner: ```js globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__ = async (environmentName, id) => { @@ -378,24 +377,24 @@ globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__ = async (environmentName, id) => { } ``` -**Custom Environment Integration:** +**Custom environment integration:** -Frameworks with custom environment setups (e.g., environments running in separate workers or with custom module loading) can override this global to provide their own module import logic. +Frameworks can override this global with their own module import logic when environments run in separate workers or use custom module loading. ```js -// Custom logic to import module between multiple environments inside worker +// Import modules between environments inside a worker. globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__ = async (environmentName, id) => { return myWorkerRunners[environmentname].import(id) } ``` -This allows `import.meta.viteRsc.loadModule` to work seamlessly with different runtime configurations without requiring changes to user code. +This override lets `import.meta.viteRsc.loadModule` support different runtime configurations without changes to application code. ## Plugin API ### `@vitejs/plugin-rsc` -- Type: `rsc: (options?: RscPluginOptions) => Plugin[]`; +- Type: `rsc: (options?: RscPluginOptions) => Plugin[]` ```js import rsc from '@vitejs/plugin-rsc' @@ -404,56 +403,56 @@ import { defineConfig } from 'vite' export default defineConfig({ plugins: [ rsc({ - // this is only a shorthand of specifying each rollup input via - // `environments[name].build.rollupOptions.input.index` + // This is shorthand for specifying each Rollup input through + // `environments[name].build.rollupOptions.input.index`. entries: { rsc: '...', ssr: '...', client: '...', }, - // by default, the plugin sets up middleware - // using `default` export of `rsc` environment `index` entry. - // this behavior can be customized by `serverHandler` option. + // By default, the plugin configures middleware that uses the default + // export of the `rsc` environment's `index` entry. Customize this + // behavior with the `serverHandler` option. serverHandler: false, - // the plugin provides build-time validation of 'server-only' and 'client-only' imports. - // this is enabled by default. See the "server-only and client-only import" section below for details. + // The plugin validates 'server-only' and 'client-only' imports at build + // time by default. See the "server-only and client-only imports" section. validateImports: true, - // by default, the plugin uses a build-time generated encryption key for - // "use server" closure argument binding. - // This can be overwritten by configuring `defineEncryptionKey` option, - // for example, to obtain a key through environment variable during runtime. - // cf. https://nextjs.org/docs/app/guides/data-security#overwriting-encryption-keys-advanced + // By default, the plugin generates an encryption key at build time for + // "use server" closure argument binding. Use `defineEncryptionKey` to + // configure another key source, such as an environment variable read at + // runtime. See: + // https://nextjs.org/docs/app/guides/data-security#overwriting-encryption-keys-advanced defineEncryptionKey: 'process.env.MY_ENCRYPTION_KEY', - // when `loadModuleDevProxy: true`, `import.meta.viteRsc.loadModule` is implemented - // through `fetch` based RPC, which allows, for example, rsc environment inside - // cloudflare workers to communicate with node ssr environment on main Vite process. + // When `loadModuleDevProxy` is true, `import.meta.viteRsc.loadModule` + // uses `fetch`-based RPC. For example, an RSC environment in Cloudflare + // Workers can communicate with a Node.js SSR environment in the main + // Vite process. loadModuleDevProxy: true, - // by default, `loadCss()` helper is injected based on certain heuristics. - // if it breaks, it can be opt-out or selectively applied based on files. + // By default, the plugin injects the `loadCss()` helper based on a set of + // heuristics. You can disable the transform or apply it selectively by file. rscCssTransform: { filter: (id) => id.includes('/my-app/') }, - // see `RscPluginOptions` for full options ... + // See `RscPluginOptions` for all available options. }), ], - // the same options can be also specified via top-level `rsc` property. - // this allows other plugin to set options via `config` hook. + // You can also specify these options through the top-level `rsc` property. + // This lets other plugins set options through the `config` hook. rsc: { // ... }, }) ``` -## RSC runtime (react-server-dom) API +## RSC Runtime (`react-server-dom`) API ### `@vitejs/plugin-rsc/rsc/server` -This module provides Vite-integrated RSC runtime APIs based on -`react-server-dom/server.edge` for use in the RSC environment: +This module provides Vite-integrated RSC runtime APIs based on `react-server-dom/server.edge` for use in the RSC environment: - `renderToReadableStream`: RSC serialization (React VDOM -> RSC stream) - `decodeAction/decodeReply/decodeFormState/loadServerAction` @@ -462,18 +461,15 @@ This module provides Vite-integrated RSC runtime APIs based on ### `@vitejs/plugin-rsc/rsc/static` -This module provides a Vite-integrated RSC runtime API based on -`react-server-dom/static.edge` for use in the RSC environment: +This module provides a Vite-integrated RSC runtime API based on `react-server-dom/static.edge` for use in the RSC environment: - `prerender`: static RSC serialization (React VDOM -> RSC stream) -`prerender` supports the same Vite-specific `onClientReference` extension as -`renderToReadableStream`. +`prerender` supports the same Vite-specific `onClientReference` extension as `renderToReadableStream`. ### `@vitejs/plugin-rsc/rsc/client` -This module provides Vite-integrated RSC runtime APIs based on -`react-server-dom/client.edge` for use in the RSC environment: +This module provides Vite-integrated RSC runtime APIs based on `react-server-dom/client.edge` for use in the RSC environment: - `createFromReadableStream`: RSC deserialization (RSC stream -> React VDOM) - `encodeReply` @@ -483,21 +479,19 @@ Together, these APIs allow a React VDOM to be serialized, saved as an RSC stream ### `@vitejs/plugin-rsc/rsc` -This module re-exports `@vitejs/plugin-rsc/rsc/server` and -`@vitejs/plugin-rsc/rsc/client`. +This module re-exports `@vitejs/plugin-rsc/rsc/server` and `@vitejs/plugin-rsc/rsc/client`. #### Vite-specific extension: `renderToReadableStream` (experimental) > [!NOTE] > This is a Vite-specific extension to the standard React RSC API. The official `react-server-dom` does not provide this callback mechanism. -`renderToReadableStream` API is extended with an optional third parameter with `onClientReference` callback. -This is invoked whenever a client reference is used in RSC stream rendering. +The `renderToReadableStream` API accepts an optional third parameter with an `onClientReference` callback. The callback runs whenever RSC stream rendering uses a client reference. ```ts function renderToReadableStream( data: T, - // standard options (e.g. temporaryReferences, onError, etc.) + // Standard options, such as temporaryReferences and onError. options?: object, // vite-specific options extraOptions?: { @@ -512,61 +506,45 @@ function renderToReadableStream( ### `@vitejs/plugin-rsc/ssr` -This module provides Vite-integrated RSC runtime APIs based on -`react-server-dom/client.edge` for use in the SSR environment: +This module provides Vite-integrated RSC runtime APIs based on `react-server-dom/client.edge` for use in the SSR environment: - `createFromReadableStream`: Deserializes an RSC stream into a React VDOM -- `encodeReply`: Serializes server function arguments +- `encodeReply`: Serializes Server Function arguments - `createTemporaryReferenceSet`: Creates a temporary reference set shared by deserialization and reply serialization ### `@vitejs/plugin-rsc/browser` -This module provides Vite-integrated RSC runtime APIs based on -`react-server-dom/client.browser` for use in the browser environment: +This module provides Vite-integrated RSC runtime APIs based on `react-server-dom/client.browser` for use in the browser environment: - `createFromReadableStream`: Deserializes an RSC stream into a React VDOM - `createFromFetch`: Deserializes an RSC response from a fetch promise -- `encodeReply`: Serializes server function arguments +- `encodeReply`: Serializes Server Function arguments - `createTemporaryReferenceSet`: Creates a temporary reference set shared by deserialization and reply serialization -- `setServerCallback`: Configures how server functions are called +- `setServerCallback`: Configures how Server Functions are called -### Low-level runtime entry points +### Low-Level Runtime Entry Points The runtime APIs are exposed through two layers: -- `@vitejs/plugin-rsc/rsc`, `@vitejs/plugin-rsc/ssr`, and - `@vitejs/plugin-rsc/browser` are the recommended entry points for application - and framework runtime code. They initialize the plugin's built-in module - loading using the manifests generated by Vite and re-export the corresponding - React runtime APIs described above. -- `@vitejs/plugin-rsc/react/rsc`, `@vitejs/plugin-rsc/react/ssr`, and - `@vitejs/plugin-rsc/react/browser` are low-level runtime adapters. They are - used by code generated for `"use client"` and `"use server"`, and by custom - integrations that provide module loading through `setRequireModule` instead - of using the plugin's generated manifests. - -The two layers share the same module loader within an environment, which allows -generated code to use `/react/*` after a top-level entry initializes the -built-in loader. Application code should continue using the top-level entries, -which re-export the same runtime APIs. Direct use of `/react/*` is only needed -by custom integrations that install their own loaders, such as the -[`examples/browser-mode`](./examples/browser-mode) example, which runs the RSC -and React client environments in the browser. +- `@vitejs/plugin-rsc/rsc`, `@vitejs/plugin-rsc/ssr`, and `@vitejs/plugin-rsc/browser` are the recommended entry points for application and framework runtime code. They initialize the plugin's built-in module loading from the manifests generated by Vite and re-export the corresponding React runtime APIs described above. +- `@vitejs/plugin-rsc/react/rsc`, `@vitejs/plugin-rsc/react/ssr`, and `@vitejs/plugin-rsc/react/browser` are low-level runtime adapters. Code generated for `"use client"` and `"use server"` uses these adapters, as do custom integrations that load modules through `setRequireModule` instead of the plugin's generated manifests. + +The two layers share the same module loader within an environment. After a top-level entry initializes the built-in loader, generated code can use `/react/*`. Application code should continue to use the top-level entries, which re-export the same runtime APIs. Only custom integrations that install their own loaders need to use `/react/*` directly. One example is [`examples/browser-mode`](./examples/browser-mode), which runs the RSC and React client environments in the browser. ## Tips ### CSS Support -The plugin automatically handles CSS code-splitting and injection for server components. This eliminates the need to manually call [`import.meta.viteRsc.loadCss()`](#importmetaviterscloadcss) in most cases. +The plugin automatically handles CSS code-splitting and injection for Server Components. This eliminates the need to manually call [`import.meta.viteRsc.loadCss()`](#importmetaviterscloadcss) in most cases. -1. **Component Detection**: The plugin automatically detects server components by looking for: - - Function exports with capital letter names (e.g., `export function Page() {}`) - - Default exports that are functions with capital names (e.g., `export default function Page() {}`) - - Const exports assigned to functions with capital names (e.g., `export const Page = () => {}`) +1. **Component detection**: The plugin automatically detects Server Components by looking for: + - Function exports with capitalized names (for example, `export function Page() {}`) + - Default exports that are functions with capitalized names (for example, `export default function Page() {}`) + - `const` exports assigned to functions with capitalized names (for example, `export const Page = () => {}`) -2. **CSS Import Detection**: For detected components, the plugin checks if the module imports any CSS files (`.css`, `.scss`, `.sass`, etc.) +2. **CSS import detection**: For each detected component, the plugin checks whether its module imports any CSS files (`.css`, `.scss`, `.sass`, and so on). -3. **Automatic Wrapping**: When both conditions are met, the plugin wraps the component with a CSS injection wrapper: +3. **Automatic wrapping**: When both conditions are met, the plugin wraps the component with a CSS injection wrapper: ```tsx // Before transformation @@ -589,9 +567,9 @@ export function Page() { } ``` -### Using different React versions +### Using Different React Versions -By default, `@vitejs/plugin-rsc` includes a [vendored version](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-rsc/package.json#L64) of `react-server-dom-webpack`. When `react-server-dom-webpack` is installed in your project's dependencies, the plugin will automatically use it instead, allowing you to use any React version you need. +By default, `@vitejs/plugin-rsc` includes a [vendored version](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-rsc/package.json#L64) of `react-server-dom-webpack`. If your project declares `react-server-dom-webpack` as a dependency, the plugin uses that version instead. This lets you choose the React version your project needs. **[Canary](https://react.dev/community/versioning-policy#canary-channel) or [experimental](https://react.dev/community/versioning-policy#experimental-channel) versions:** @@ -605,7 +583,7 @@ By default, `@vitejs/plugin-rsc` includes a [vendored version](https://github.co } ``` -**Specific versions (e.g., for security updates):** +**Specific versions, such as for security updates:** ```json { @@ -617,15 +595,15 @@ By default, `@vitejs/plugin-rsc` includes a [vendored version](https://github.co } ``` -### Using `@vitejs/plugin-rsc` as a framework package's `dependencies` +### Using `@vitejs/plugin-rsc` as a Framework Dependency -By default, `@vitejs/plugin-rsc` is expected to be used as `peerDependencies` similar to `react` and `react-dom`. When `@vitejs/plugin-rsc` is not available at the project root (e.g., in `node_modules/@vitejs/plugin-rsc`), you will see warnings like: +Like `react` and `react-dom`, `@vitejs/plugin-rsc` is normally declared in a framework package's `peerDependencies`. If `@vitejs/plugin-rsc` is not available at the project root, such as at `node_modules/@vitejs/plugin-rsc`, you will see warnings like this: ```sh Failed to resolve dependency: @vitejs/plugin-rsc/vendor/react-server-dom/client.browser, present in client 'optimizeDeps.include' ``` -This can be fixed by updating `optimizeDeps.include` to reference `@vitejs/plugin-rsc` through your framework package. For example, you can add the following plugin: +To fix the warning, update `optimizeDeps.include` to reference `@vitejs/plugin-rsc` through your framework package. For example, add the following plugin: ```js // package name is "my-rsc-framework" @@ -648,9 +626,9 @@ export default function myRscFrameworkPlugin() { } ``` -### Typescript +### TypeScript -Types for global API are defined in `@vitejs/plugin-rsc/types`. For example, you can add it to `tsconfig.json` to have types for `import.meta.viteRsc` APIs: +Types for the global APIs are defined in `@vitejs/plugin-rsc/types`. Add this package to `tsconfig.json` to enable types for the `import.meta.viteRsc` APIs: ```json { @@ -666,16 +644,15 @@ import.meta.viteRsc.loadModule // (environmentName: string, entryName?: string) => Promise ``` -See also [Vite documentation](https://vite.dev/guide/api-hmr.html#intellisense-for-typescript) for `vite/client` types. +See the [Vite documentation](https://vite.dev/guide/api-hmr.html#intellisense-for-typescript) for information about the `vite/client` types. -### `server-only` and `client-only` import +### `server-only` and `client-only` Imports -You can use the `server-only` import to prevent accidentally importing server-only code into client bundles, which can expose sensitive server code in public static assets. -For example, the plugin will show an error `'server-only' cannot be imported in client build` for the following code: +Use the `server-only` import to prevent server-only code from being imported into client bundles, where public static assets could expose sensitive code. For example, the plugin reports the error `'server-only' cannot be imported in client build` for the following code: - server-utils.js @@ -700,8 +677,7 @@ import { getData } from './server-utils.js' // ❌ 'server-only' cannot be impor ... ``` -Similarly, the `client-only` import ensures browser-specific code isn't accidentally imported into server environments. -For example, the plugin will show an error `'client-only' cannot be imported in server build` for the following code: +Similarly, the `client-only` import prevents browser-specific code from being imported into server environments. The plugin reports the error `'client-only' cannot be imported in server build` for the following code: - client-utils.js @@ -725,14 +701,13 @@ export function ServerComponent() { } ``` -Note that while there are official npm packages [`server-only`](https://www.npmjs.com/package/server-only) and [`client-only`](https://www.npmjs.com/package/client-only) created by React team, they don't need to be installed. The plugin internally overrides these imports and surfaces their runtime errors as build-time errors. +Although the React team publishes the [`server-only`](https://www.npmjs.com/package/server-only) and [`client-only`](https://www.npmjs.com/package/client-only) npm packages, you do not need to install them. The plugin overrides these imports internally and reports their runtime errors at build time. -This build-time validation is enabled by default and can be disabled by setting `validateImports: false` in the plugin options. +This build-time validation is enabled by default. To disable it, set `validateImports: false` in the plugin options. ## Credits -This project builds on fundamental techniques and insights from pioneering Vite RSC implementations. -Additionally, Parcel and React Router's work on standardizing the RSC bundler/app responsibility has guided this plugin's API design: +This project builds on techniques and insights from pioneering Vite RSC implementations. Work by Parcel and React Router to standardize the responsibilities of RSC bundlers and applications has also guided the plugin's API design: - [Waku](https://github.com/wakujs/waku) - [@lazarv/react-server](https://github.com/lazarv/react-server)