
Security News
Open VSX Begins Implementing Pre-Publish Security Checks After Repeated Supply Chain Incidents
Following multiple malicious extension incidents, Open VSX outlines new safeguards designed to catch risky uploads earlier.
@vitejs/plugin-rsc
Advanced tools
This package provides React Server Components (RSC) support for Vite.
react-server-dom) API without framework-specific abstractions.@cloudflare/vite-plugin).You can create a starter project by:
npx degit vitejs/vite-plugin-react/packages/plugin-rsc/examples/starter my-app
Start here: ./examples/starter - Recommended for understanding the plugin
Integration examples:
./examples/basic - Advanced RSC features and testing
"use cache" example)../examples/ssg - Static site generation with MDX and client components for interactivity../examples/react-router - React Router RSC integration
This example is a simplified version of ./examples/starter. You can read ./examples/starter/src/framework/entry.{rsc,ssr,browser}.tsx for more in-depth commentary, which includes server function handling and client-side RSC re-fetching/re-rendering.
This is the diagram to show the basic flow of RSC rendering process. See also https://github.com/hi-ogawa/vite-plugins/discussions/606.
graph TD
subgraph "<strong>rsc environment</strong>"
A["React virtual dom tree"] --> |"[@vitejs/plugin-rsc/rsc]<br /><code>renderToReadableStream</code>"| B1["RSC Stream"];
end
B1 --> B2
B1 --> B3
subgraph "<strong>ssr environment</strong>"
B2["RSC Stream"] --> |"[@vitejs/plugin-rsc/ssr]<br /><code>createFromReadableStream</code>"| C1["React virtual dom tree"];
C1 --> |"[react-dom/server]<br/>SSR"| E["HTML String/Stream"];
end
subgraph "<strong>client environment</strong>"
B3["RSC Stream"] --> |"[@vitejs/plugin-rsc/browser]<br /><code>createFromReadableStream</code>"| C2["React virtual dom tree"];
C2 --> |"[react-dom/client]<br/>CSR: mount, hydration"| D["DOM Elements"];
end
style A fill:#D6EAF8,stroke:#333,stroke-width:2px
style B1 fill:#FEF9E7,stroke:#333,stroke-width:2px
style B2 fill:#FEF9E7,stroke:#333,stroke-width:2px
style B3 fill:#FEF9E7,stroke:#333,stroke-width:2px
style C1 fill:#D6EAF8,stroke:#333,stroke-width:2px
style C2 fill:#D6EAF8,stroke:#333,stroke-width:2px
style D fill:#D5F5E3,stroke:#333,stroke-width:2px
style E fill:#FADBD8,stroke:#333,stroke-width:2px
import rsc from '@vitejs/plugin-rsc'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
// add plugin
rsc(),
],
// specify entry point for each environment.
environments: {
// `rsc` environment loads modules with `react-server` condition.
// this environment is responsible for:
// - RSC stream serialization (React VDOM -> RSC stream)
// - server functions handling
rsc: {
build: {
rollupOptions: {
input: {
index: './src/framework/entry.rsc.tsx',
},
},
},
},
// `ssr` environment loads modules without `react-server` condition.
// this environment is responsible for:
// - RSC stream deserialization (RSC stream -> React VDOM)
// - traditional SSR (React VDOM -> HTML string/stream)
ssr: {
build: {
rollupOptions: {
input: {
index: './src/framework/entry.ssr.tsx',
},
},
},
},
// client environment is used for 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
client: {
build: {
rollupOptions: {
input: {
index: './src/framework/entry.browser.tsx',
},
},
},
},
},
})
import { renderToReadableStream } from '@vitejs/plugin-rsc/rsc'
// the plugin assumes `rsc` entry having default export of request handler
export default async function handler(request: Request): Promise<Response> {
// serialize React VDOM to RSC stream
const root = (
<html>
<body>
<h1>Test</h1>
</body>
</html>
)
const rscStream = renderToReadableStream(root)
// respond direct RSC stream request based on framework's convention
if (request.url.endsWith('.rsc')) {
return new Response(rscStream, {
headers: {
'Content-type': 'text/x-component;charset=utf-8',
},
})
}
// delegate to SSR environment for html rendering
// `loadModule` is a helper API provided by the plugin for multi environment interaction.
const ssrEntry = await import.meta.viteRsc.loadModule<
typeof import('./entry.ssr.tsx')
>('ssr', 'index')
const htmlStream = await ssrEntry.handleSsr(rscStream)
// respond html
return new Response(htmlStream, {
headers: {
'Content-type': 'text/html',
},
})
}
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
const root = await createFromReadableStream(rscStream)
// helper API to allow referencing browser entry content from SSR environment
const bootstrapScriptContent =
await import.meta.viteRsc.loadBootstrapScriptContent('index')
// render html (traditional SSR)
const htmlStream = renderToReadableStream(root, {
bootstrapScriptContent,
})
return htmlStream
}
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
const rscResponse = await fetch(window.location.href + '.rsc')
const root = await createFromReadableStream(rscResponse.body)
// hydration (traditional CSR)
hydrateRoot(document, root)
}
main()
The plugin provides an additional helper for multi environment interaction.
rsc or ssr environmentimport.meta.viteRsc.loadModule(environmentName: "ssr" | "rsc", entryName: string) => Promise<T>This allows importing ssr environment module specified by environments.ssr.build.rollupOptions.input[entryName] inside rsc environment and vice versa.
During development, by default, this API assumes both rsc and ssr environments execute under the main Vite process. 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.
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.
For example,
// ./entry.rsc.tsx
const ssrModule = await import.meta.viteRsc.loadModule("ssr", "index");
ssrModule.renderHTML(...);
// ./entry.ssr.tsx (with environments.ssr.build.rollupOptions.input.index = "./entry.ssr.tsx")
export function renderHTML(...) {}
rsc environmentimport.meta.viteRsc.loadCss[!NOTE] The plugin automatically injects CSS for server components. See the CSS Support section for detailed information about automatic CSS injection.
(importer?: string) => React.ReactNodeThis allows collecting css which is imported through a current server module and injecting them inside server components.
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"
return (
<>
{import.meta.viteRsc.loadCss()}
...
</>
)
}
When specifying loadCss(<id>), it will collect css through the server module resolved by <id>.
// virtual:my-framework-helper
export function Assets() {
return <>
{import.meta.viteRsc.loadCss("/routes/home.tsx")}
{import.meta.viteRsc.loadCss("/routes/about.tsx")}
{...}
</>
}
// user-app.tsx
import { Assets } from "virtual:my-framework-helper";
export function UserApp() {
return <html>
<head>
<Assets />
</head>
<body>...</body>
</html>
}
<id>?vite-rsc-css-export=<name>This special query convention provides automatic injection of import.meta.viteRsc.loadCss.
For example,
// my-route.tsx
export function Page(props) {
return <div>...</div>
}
// my-route.tsx?vite-rsc-css-export=Page
function Page(props) {
return <div>...</div>
}
function __Page(props) {
return (
<>
{import.meta.viteRsc.loadCss()}
<Page {...props} />
</>
)
}
export { __Page as Page }
ssr environmentimport.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
import { renderToReadableStream } from 'react-dom/server.edge'
const bootstrapScriptContent =
await import.meta.viteRsc.loadBootstrapScriptContent('index')
const htmlStream = await renderToReadableStream(reactNode, {
bootstrapScriptContent,
})
client environmentrsc:update eventThis event is fired when server modules are updated, which can be used to trigger re-fetching and re-rendering of RSC components on browser.
import { createFromFetch } from '@vitejs/plugin-rsc/browser'
import.meta.hot.on('rsc:update', async () => {
// re-fetch RSC stream
const rscPayload = await createFromFetch(fetch(window.location.href + '.rsc'))
// re-render ...
})
@vitejs/plugin-rscimport rsc from '@vitejs/plugin-rsc'
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`
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.
serverHandler: false,
// 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.
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.
rscCssTransform: { filter: (id) => id.includes('/my-app/') },
// 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
defineEncryptionKey: 'process.env.MY_ENCRYPTION_KEY',
}),
],
})
@vitejs/plugin-rsc/rscThis module re-exports RSC runtime API provided by react-server-dom/server.edge and react-server-dom/client.edge such as:
renderToReadableStream: RSC serialization (React VDOM -> RSC stream)createFromReadableStream: RSC deserialization (RSC stream -> React VDOM). This is also available on rsc environment itself. For example, it allows saving serialized RSC and deserializing it for later use.decodeAction/decodeReply/decodeFormState/loadServerAction/createTemporaryReferenceSetencodeReply/createClientTemporaryReferenceSet@vitejs/plugin-rsc/ssrThis module re-exports RSC runtime API provided by react-server-dom/client.edge
createFromReadableStream: RSC deserialization (RSC stream -> React VDOM)@vitejs/plugin-rsc/browserThis module re-exports RSC runtime API provided by react-server-dom/client.browser
createFromReadableStream: RSC deserialization (RSC stream -> React VDOM)createFromFetch: a robust way of createFromReadableStream((await fetch("...")).body)encodeReply/setServerCallback: server function related...[!NOTE] High level API is deprecated. Please write on your own
@vitejs/plugin-rsc/{rsc,ssr,browser}integration.
This is a wrapper of react-server-dom API and helper API to setup a minimal RSC app without writing own framework code like ./examples/starter/src/framework. See ./examples/basic for how this API is used.
@vitejs/plugin-rsc/extra/rscrenderRequest@vitejs/plugin-rsc/extra/ssrrenderHtml@vitejs/plugin-rsc/extra/browserhydrateThe plugin automatically handles CSS code-splitting and injection for server components. This eliminates the need to manually call import.meta.viteRsc.loadCss() in most cases.
Component Detection: The plugin automatically detects server components by looking for:
export function Page() {})export default function Page() {})export const Page = () => {})CSS Import Detection: For detected components, the plugin checks if the module imports any CSS files (.css, .scss, .sass, etc.)
Automatic Wrapping: When both conditions are met, the plugin wraps the component with a CSS injection wrapper:
// Before transformation
import './styles.css'
export function Page() {
return <div>Hello</div>
}
// After transformation
import './styles.css'
export function Page() {
return (
<>
{import.meta.viteRsc.loadCss()}
<div>Hello</div>
</>
)
}
See https://github.com/vitejs/vite-plugin-react/pull/524 for how to install the package for React canary and experimental usages.
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:
FAQs
React Server Components (RSC) support for Vite.
The npm package @vitejs/plugin-rsc receives a total of 47,906 weekly downloads. As such, @vitejs/plugin-rsc popularity was classified as popular.
We found that @vitejs/plugin-rsc demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Following multiple malicious extension incidents, Open VSX outlines new safeguards designed to catch risky uploads earlier.

Research
/Security News
Threat actors compromised four oorzc Open VSX extensions with more than 22,000 downloads, pushing malicious versions that install a staged loader, evade Russian-locale systems, pull C2 from Solana memos, and steal macOS credentials and wallets.

Security News
Lodash 4.17.23 marks a security reset, with maintainers rebuilding governance and infrastructure to support long-term, sustainable maintenance.