logo
  • Guide
  • API
  • Blog
  • English
    • 简体中文
    • English
    • @esmx/core
      Esmx
      App
      RenderContext
      ModuleConfig
      PackConfig
      ManifestJson
      @esmx/router
      Router
      Route
      Route Configuration
      RouterLink
      Navigation Guards
      Dynamic Route Matching
      Nested Routes
      Programmatic Navigation
      Scroll Behavior
      Layer Routing
      MicroApp
      Error Types
      Types Reference
      @esmx/router-vue
      RouterPlugin
      Composables
      Components
      Type Augmentation
      @esmx/router-react
      Micro-App Integration
      Hooks & Context
      Components
      SSR
      App
      @esmx/rspack
      @esmx/rspack-vue
      @esmx/rspack-react

      Last Updated: 4/7/2026, 2:16:07 AM

      Previous pageComponentsNext page@esmx/rspack

      #SSR

      #Introduction

      Server-side rendering with @esmx/router and React uses the renderToString() lifecycle hook in the micro-app's apps callback. The router operates in memory mode on the server, navigates to the requested URL, renders the app to HTML, and sends it to the client for hydration.

      #Server Entry

      import type { RenderContext } from '@esmx/core';
      import { Router, RouterMode } from '@esmx/router';
      import { createElement } from 'react';
      import { renderToString } from 'react-dom/server';
      import App from './App';
      import { routes } from './routes';
      
      export default async (rc: RenderContext) => {
          const router = new Router({
              mode: RouterMode.memory,
              base: new URL(rc.params.url, 'http://localhost'),
              routes,
              apps: (router) => ({
                  mount(el) { /* client only */ },
                  unmount() { /* client only */ },
                  async renderToString() {
                      return renderToString(createElement(App, { router }));
                  }
              })
          });
      
          await router.replace(rc.params.url);
          const html = await router.renderToString();
      
          // Collect route data for client hydration
          const routeData = router.route.data;
      
          rc.html = `<!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          ${rc.preload()}
          ${rc.css()}
      </head>
      <body>
          <div id="app">${html}</div>
          ${routeData ? `<script>window.__ROUTE_DATA__ = ${JSON.stringify(routeData)}</script>` : ''}
          ${rc.importmap()}
          ${rc.moduleEntry()}
          ${rc.modulePreload()}
      </body>
      </html>`;
      };

      #Client Hydration

      import { Router, RouterMode } from '@esmx/router';
      import { hydrateRoot } from 'react-dom/client';
      import { createElement } from 'react';
      import App from './App';
      import { routes } from './routes';
      
      const router = new Router({
          root: '#app',
          mode: RouterMode.history,
          routes,
          data: (window as any).__ROUTE_DATA__,
          apps: (router) => ({
              mount(el) {
                  hydrateRoot(el, createElement(App, { router }));
              },
              unmount() { /* handle cleanup */ },
              async renderToString() {
                  const { renderToString } = await import('react-dom/server');
                  return renderToString(createElement(App, { router }));
              }
          })
      });