Skip to main content

Overview

COSMOS RSC uses a two-phase rendering architecture that combines React Server Components (RSC) and traditional server-side rendering (SSR) to deliver interactive applications with optimal performance.

Rendering Architecture

Phase 1: RSC Rendering

The first phase renders React Server Components and generates an RSC payload:
// From core/server/index.js
const { renderToPipeableStream } = require('react-server-dom-webpack/server');

const payload = {
  rootLayout,
  tree,
  serverActionResult,
  formState,
};

const rscStream = renderToPipeableStream(payload, webpackMap, {
  onError: (error) => {
    console.error('Render error:', error);
  },
});

Phase 2: SSR (HTML Generation)

The second phase consumes the RSC payload and generates HTML:
// From core/server/lib/fizz-worker.js
const { renderToPipeableStream } = require('react-dom/server');

const htmlStream = renderToPipeableStream(
  createElement(SSRApp, {
    initialState: { tree },
    rootLayout,
  }),
  {
    formState,
    bootstrapScripts: ['/client.js'],
    onShellReady: () => {
      htmlStream
        .pipe(injectRSCPayload(payloadConsumerRSCStream))
        .pipe(writableStream);
    },
  }
);

Render Phases

COSMOS RSC manages three distinct render phases during request handling:

START

Initial phase when a request is received. The server initializes the app store with cookie data and metadata.
const appStore = {
  metadata: {
    renderPhase: 'START',
  },
  cookies: {
    incoming: incomingCookies,
    outgoing: new Map(),
  },
};

SERVER_ACTION

Executed when processing server actions (POST requests). During this phase:
  • Server actions are decoded and executed
  • Cookies can be modified
  • Form state is processed
if (req.method === 'POST') {
  metadata.renderPhase = 'SERVER_ACTION';
  
  const serverActionId = req.headers['server-action-id'];
  const args = await decodeReplyFromBusboy(bb);
  serverActionResult = await serverAction.apply(null, args);
}

RSC

The main rendering phase where React Server Components are rendered:
  • Server Components are executed
  • Data fetching occurs
  • RSC payload is generated
  • Cookies are read-only (cannot be modified)
metadata.renderPhase = 'RSC';

const tree = createElement(Page, { searchParams: { ...req.query } });
const rscStream = renderToPipeableStream(payload, webpackMap, options);

Content-Type Handling

RSC-Only Response

When the client requests only the RSC payload (for client-side navigation):
if (req.headers.accept === 'text/x-component') {
  res.setHeader('Content-Type', 'text/x-component');
  rscStream.pipe(res);
  return;
}

Full HTML Response

For initial page loads, the server generates full HTML with the RSC payload embedded:
res.setHeader('Content-Type', 'text/html');

const passThroughRSCStream = new PassThrough();
rscStream.pipe(passThroughRSCStream);

// HTML stream with embedded RSC payload
htmlStream
  .pipe(injectRSCPayload(payloadConsumerRSCStream))
  .pipe(writableStream);

Worker-Based Architecture

COSMOS RSC uses a worker thread for SSR rendering to isolate the HTML generation process:

Main Thread (RSC Rendering)

const fizzWorker = new Worker(FIZZ_WORKER_PATH, {
  execArgv: ['--conditions', 'default'],
});

const { port1, port2 } = new MessageChannel();
fizzWorker.postMessage(request, [port2]);

passThroughRSCStream.on('data', (data) => {
  port1.postMessage({
    type: 'data',
    data,
  });
});

Worker Thread (SSR)

parentPort.on('message', async (request) => {
  request.port.on('message', (message) => {
    if (message.type === 'data') {
      htmlConsumerRSCStream.write(message.data);
    } else if (message.type === 'end') {
      htmlConsumerRSCStream.end();
    }
  });
});

Request Flow

  1. Request received - Express middleware handles incoming HTTP requests
  2. Cookie parsing - Incoming cookies are extracted from request headers
  3. App store initialization - Request context is established
  4. Server action execution (if POST) - Actions are decoded and executed
  5. Page component resolution - Dynamic import of the requested page
  6. RSC rendering - Server Components are rendered to RSC stream
  7. SSR rendering (if HTML requested) - RSC payload is consumed to generate HTML
  8. Response streaming - Content is streamed to the client

Page Resolution

Pages are dynamically imported based on the request path:
const pagePath = `../../app/pages${req.path}`;
let Page;

try {
  Page = require(pagePath).default;
} catch (error) {
  logger.error(`Failed to import page: ${pagePath}`, error);
  res.status(500).send('Internal Server Error');
  return;
}

const tree = createElement(Page, { searchParams: { ...req.query } });
Pages must export a default component to be rendered. The component receives searchParams as props containing URL query parameters.

Server Actions

Server actions can be invoked in two ways:

Programmatic Actions

const serverActionId = req.headers['server-action-id'];
const [fileUrl, functionName] = serverActionId.split('#');
const serverAction = require(fileURLToPath(fileUrl))[functionName];
const args = await decodeReplyFromBusboy(bb);
serverActionResult = await serverAction.apply(null, args);

Form Actions

const fakeReq = new Request('http://localhost', {
  method: 'POST',
  headers: { 'Content-Type': req.headers['content-type'] },
  body: Readable.toWeb(req),
  duplex: 'half',
});
const formData = await fakeReq.formData();
const action = await decodeAction(formData);
const result = await action();
formState = await decodeFormState(result, formData);

Error Handling

The rendering pipeline includes error handling at multiple levels:
renderToPipeableStream(payload, webpackMap, {
  onError: (error) => {
    console.error('Render error:', error);
    res.end();
  },
});
Render errors will terminate the response stream. Ensure proper error boundaries are in place in your React components.

Static Assets

Static files from the build directory are served automatically:
app.use(express.static(BUILD_DIR));
The client bundle is automatically included in the HTML response:
bootstrapScripts: ['/client.js']

Build docs developers (and LLMs) love