Overview
COSMOS RSC is built on React Server Components (RSC) and provides a complete framework for building server-rendered React applications. The architecture separates client and server concerns while enabling seamless integration between them.
Core architecture
The framework is divided into three main layers:
Server layer
The server layer handles RSC rendering, routing, and server actions. It runs on Node.js with Express.
const app = express ();
async function requestHandler ( req , res ) {
// Map URL path to page component
const pagePath = `../../app/pages ${ req . path } ` ;
const Page = require ( pagePath ). default ;
// Create React tree
const tree = createElement ( Page , { searchParams: { ... req . query } });
// Render to RSC stream
const webpackMap = await getReactClientManifest ();
const rscStream = renderToPipeableStream ({ tree }, webpackMap );
// Handle different response types
if ( req . headers . accept === 'text/x-component' ) {
// Return RSC payload for client navigations
res . setHeader ( 'Content-Type' , 'text/x-component' );
rscStream . pipe ( res );
} else {
// Return HTML for initial page loads
res . setHeader ( 'Content-Type' , 'text/html' );
// Process through Fizz worker for HTML streaming
}
}
Key responsibilities:
File-system based routing
React Server Components rendering
Server action execution
Cookie management
HTML streaming with Suspense
Client layer
The client layer hydrates the initial HTML and handles client-side navigation.
import { hydrateRoot } from 'react-dom/client' ;
import { createFromReadableStream } from 'react-server-dom-webpack/client' ;
async function hydrateDocument () {
// Parse RSC payload from embedded stream
const { rootLayout , tree , formState } = await createFromReadableStream (
rscStream ,
{ callServer }
);
// Cache initial page
routerCache . set ( getFullPath ( window . location . href ), tree );
// Hydrate React tree
const app = (
< ErrorBoundary >
< BrowserApp rootLayout = { rootLayout } initialState = { { tree } } />
</ ErrorBoundary >
);
hydrateRoot ( document , app , { formState });
}
Key responsibilities:
Hydrating server-rendered HTML
Client-side navigation
Router cache management
Server action invocation
Build layer
The build layer uses Webpack to bundle client code and generate manifests.
core/build/webpack.config.js
module . exports = {
entry: [
path . resolve ( __dirname , '../client/index.js' ),
path . resolve ( __dirname , '../../app/globals.css' ),
],
output: {
path: path . resolve ( __dirname , '../../.cosmos-rsc' ),
filename: 'client.js' ,
},
plugins: [
new ReactServerWebpackPlugin ({
isServer: false ,
clientReferences: [
{
directory: './app' ,
recursive: true ,
include: / \. js $ / ,
},
],
}),
],
};
Key responsibilities:
Bundling client JavaScript
Processing CSS with Tailwind
Generating React Client Manifest
Identifying client components
RSC rendering pipeline
The rendering pipeline coordinates between server and client:
Request handling : Express receives request and maps URL to page component
RSC rendering : React renders components to RSC stream format
HTML streaming : Fizz worker converts RSC stream to HTML
Payload injection : RSC data embedded into HTML as inline scripts
Client hydration : Browser parses RSC payload and hydrates React tree
Interactive : Client-side navigation and server actions work seamlessly
Worker threads
COSMOS RSC uses worker threads to enable concurrent HTML rendering:
core/server/lib/fizz-worker.js
const { parentPort } = require ( 'worker_threads' );
parentPort . on ( 'message' , async ( request ) => {
// Consume RSC stream
const { rootLayout , tree } = await createFromNodeStream (
htmlConsumerRSCStream ,
serverConsumerManifest
);
// Render to HTML
const htmlStream = renderToPipeableStream (
createElement ( SSRApp , { initialState: { tree }, rootLayout }),
{
bootstrapScripts: [ '/client.js' ],
onShellReady : () => {
htmlStream
. pipe ( injectRSCPayload ( payloadConsumerRSCStream ))
. pipe ( writableStream );
},
}
);
});
The Fizz worker:
Runs in a separate thread from the main server
Consumes the RSC stream
Renders to HTML using React DOM Server
Injects RSC payload into the HTML
Client/server split
Components are automatically split between client and server:
Server Component
Client Component
// Runs only on server - no 'use client' directive
async function UserProfile () {
const userData = await fetchUserData ();
return < div > { userData . name } </ div > ;
}
The ReactServerWebpackPlugin identifies client components and generates a manifest mapping module IDs to bundled chunks.
Communication patterns
Server to client
Server components pass data to client components through props:
async function ServerParent () {
const data = await fetchData ();
return < ClientChild data = { data } /> ;
}
Client to server
Client components call server actions to execute server code:
'use client' ;
function ClientForm () {
async function handleSubmit () {
const result = await serverAction ();
}
return < button onClick = { handleSubmit } > Submit </ button > ;
}
Server actions use the callServer function which dispatches through the app reducer to make a POST request to the server.
Directory structure
Your application code lives in the app/ directory:
app/
├── pages/ # File-system routes
├── actions/ # Server action modules
├── components/ # Shared components
├── root-layout.js # Root HTML layout
└── globals.css # Global styles
The framework code lives in core/:
core/
├── server/ # Server runtime
├── client/ # Client runtime
├── build/ # Build configuration
└── rsc-html-stream/ # Stream utilities
Next steps
Routing Learn about file-system routing
Server Components Understand React Server Components