website/docs/en/guide/tech/rsc.mdx
import { PackageManagerTabs } from '@theme';
Server Components are a new type of Component that renders ahead of time, before bundling, in an environment separate from your client app or SSR server. Rspack v2.0.0 and later have built-in full support for Server Components.
Rspack provides two solutions to support React Server Components:
The rstack-examples repository contains full examples of building React Server Component applications using Rspack.
:::tip
React Server Components rely on the new architecture of React 19. Please ensure that both react and react-dom are version v19.1.0 or higher.
:::
React Server Component artifacts require cooperative execution across both server and browser environments. To support this architecture, Rspack launches two Compiler instances to separately build the server and browser artifacts. The diagram below illustrates the overall build flow:
target: 'web'): Responsible for generating code that runs in the browser. It primarily bundles Client Components, as well as CSS and static resources required by the application, and handles the hydration logic for components.target: 'node'): Responsible for generating code that runs on the server. It handles the pre-rendering of React Server Components, bundles the execution logic for Server Actions, and handles code related to Server-Side Rendering (SSR).In the configuration file, this corresponds to two Compiler configurations:
export default [
// Client Compiler: Builds browser-side resources
{
target: 'web',
// ...client-specific config
},
// Server Compiler: Builds RSC Payload and Server Actions
{
target: 'node',
// ...server-specific config
},
];
Rspack supports React Server Components through a pair of coordinating plugins. You must call rsc.createPlugins() once to get ServerPlugin and ClientPlugin, then attach each to the corresponding Compiler:
import { experiments } from '@rspack/core';
const { createPlugins, Layers } = experiments.rsc;
const { ServerPlugin, ClientPlugin } = createPlugins();
export default [
{
// Client Compiler
target: 'web',
plugins: [new ClientPlugin()],
// ...client-specific config
},
{
// Server Compiler
target: 'node',
plugins: [new ServerPlugin()],
// ...server-specific config
},
];
These two plugins internally schedule the build processes of the Client Compiler and Server Compiler, establishing a communication mechanism to synchronize compilation state, module info, and generate the client reference manifest.
You need to configure entries for the Client Compiler and Server Compiler respectively. There must be an entry in the Client Compiler with the exact same name as the one in the Server Compiler.
export default [
{
target: 'web',
entry: {
// A 'main' entry with the same name as the server entry must exist,
// acting as the module graph root node for the client RSC runtime.
main: { import: './src/entry.client.tsx' },
},
},
{
target: 'node',
entry: {
// The server entry name is 'main'
main: { import: './src/entry.rsc.tsx' },
},
},
];
When the Server Compiler finds "use client" components, it delegates those modules to the Client Compiler. Because the client config can have multiple entries, the RSC plugin uses a same-name convention: the client entry that has the same name as a server entry is treated as the RSC runtime mount for that entry.
Discovered "use client" modules are injected into that matching entry’s module graph. If no client entry with the same name exists, the plugin reports an "RSC Client Entry Mismatch" diagnostic and client components will not load correctly in the browser.
In the Server Compiler, Rspack uses layer to distinguish RSC from SSR. The RSC plugin uses these layer names:
Layers.rsc ('react-server-components'): RSC environment. Only modules in this layer have "use client" turned into client references and "use server" registered as Server Actions.Layers.ssr ('server-side-rendering'): SSR environment. Use this to mark the SSR entry; if no modules are assigned to this layer, Rspack skips SSR output.import path from 'node:path';
const ssrEntry = path.resolve(
import.meta.dirname,
'src/framework/entry.ssr.tsx',
);
const rscEntry = path.resolve(
import.meta.dirname,
'src/framework/entry.rsc.tsx',
);
export default [
// ...
{
target: 'node',
module: {
rules: [
{ resource: ssrEntry, layer: Layers.ssr },
{
resource: rscEntry,
layer: Layers.rsc,
resolve: { conditionNames: ['react-server', '...'] },
},
{
issuerLayer: Layers.rsc,
exclude: ssrEntry,
resolve: { conditionNames: ['react-server', '...'] },
},
],
},
// ...
},
];
"use server-entry" is a specific directive introduced by Rspack, designed primarily for framework developers. It is used to mark a Server Component as the logical entry point for a page or route.
When compiling a component with "use server-entry", Rspack generates the complete set of resources required to initialize the RSC application in the browser and records this information by mounting static properties directly onto the exported component:
'use server-entry';
import './Todos.css';
import { Dialog } from './Dialog';
import { TodoDetail } from './TodoDetail';
import { TodoCreate } from './TodoCreate';
import { TodoList } from './TodoList';
export async function Todos({ id }: { id?: number }) {
// ...component logic
}
During the server runtime, you can directly access these static properties mounted on the component to construct HTML head resources:
import type { ServerEntry } from 'react-server-dom-rspack/server.node';
const { Todos } = await import('../Todos.tsx');
const serverEntry = Todos as ServerEntry<typeof Todos>;
const root = (
<>
{serverEntry.entryCssFiles
? serverEntry.entryCssFiles.map((href) => (
<link
key={href}
rel="stylesheet"
href={href}
precedence="default"
></link>
))
: null}
<Todos id={id} />
</>
);
const response = await handleRequest({
request,
getRoot: () => root,
// Read static properties from the component to use as bootstrap scripts
bootstrapScripts: serverEntry.entryJsFiles,
nonce,
});
Add builtin:swc-loader for .jsx / .tsx and enable rspackExperiments.reactServerComponents so Rspack parses and transforms RSC directives such as "use client", "use server", and "use server-entry".
const rscRule = {
test: /\.(?:js|mjs|jsx|ts|tsx)$/,
use: {
loader: 'builtin:swc-loader',
options: {
detectSyntax: 'auto',
},
rspackExperiments: {
reactServerComponents: true,
},
},
type: 'javascript/auto',
};
export default [
{
target: 'web',
module: {
rules: [rscRule],
},
},
{
target: 'node',
module: {
rules: [rscRule],
},
},
];
The loader executes different transformation logic for "use client" and "use server" directives based on the layer to which the module belongs:
When the module belongs to Layers.rsc (Server Component environment), react-server-dom-rspack is invoked for transformation:
"use client" are transformed into Client References."use server" are registered as Server Actions, enabling them to respond to remote calls from the client.When the module does not belong to Layers.rsc (e.g., client or SSR environments):
"use server": The module is transformed into a Server Reference.The RSC plugin uses the loader’s directive info to tell client and server components apart, and uses it to inject client entries, collect Server Actions, and generate the RSC manifest.
The RSC architecture requires handling client and server builds, responding to RSC requests, and managing server component HMR. Consequently, Rspack's built-in Dev Server cannot meet these requirements. You need to implement a custom development server to provide the following core capabilities:
To support Server Component HMR, Rspack provides the onServerComponentChanges hook on ServerPlugin (it runs after a build when server component output has changed). In your custom dev server, use this callback to notify the browser (e.g. via WebSocket) so it can re-request RSC and refresh the page.
export default [
// ...
{
target: 'node',
plugins: [
new ServerPlugin({
onServerComponentChanges() {
// Invoke a method on your custom server to notify the client to refresh
},
}),
],
},
];