React SSR
@nano_kit/react-ssr is the React adapter for server-side rendering in Nano Kit.
Installation
Section titled “Installation”pnpm add @nano_kit/store @nano_kit/router @nano_kit/react @nano_kit/react-router @nano_kit/react-ssr react react-domyarn add @nano_kit/store @nano_kit/router @nano_kit/react @nano_kit/react-router @nano_kit/react-ssr react react-domnpm install @nano_kit/store @nano_kit/router @nano_kit/react @nano_kit/react-router @nano_kit/react-ssr react react-domQuick Start
Section titled “Quick Start”-
Define your app
Create an index file that exports
routesandpages. This file is the single source of truth consumed by both the server renderer and the Vite plugin.// src/index.tsimport { page, layout, loadable } from '@nano_kit/router'import * as Layout from './Layout.jsx'export const routes = {home: '/',about: '/about'} as constexport const pages = [layout(Layout, [page('home', loadable(() => import('./pages/Home.jsx'))),page('about', loadable(() => import('./pages/About.jsx')))])]declare module '@nano_kit/router' {interface AppContext {routes: typeof routes}} -
Set up the Vite plugin
Add
@nano_kit/react-ssr/vite-pluginto your Vite config. No extra configuration is needed for a standard setup — the plugin uses built-in client and renderer templates automatically.// vite.config.jsimport { defineConfig } from 'vite'import react from '@vitejs/plugin-react'import ssr from '@nano_kit/react-ssr/vite-plugin'export default defineConfig({plugins: [react(),ssr({ index: 'src/index.ts' })]})vite dev— starts the development server with SSR rendering handled in-process.vite build— producesdist/client/(browser assets) anddist/renderer/(SSR renderer bundle).
-
Write your production HTTP server
For production, write your own HTTP server that imports the built renderer and calls
renderer.render(url)for every incoming request:// server.jsimport { renderer } from './dist/renderer/index.js'// Express exampleapp.get('*', async (req, res) => {const result = await renderer.render(req.url)if (result.redirect) {return res.redirect(result.statusCode, result.redirect)}if (result.html !== null) {return res.status(result.statusCode).send(result.html)}res.status(result.statusCode).send('Not Found')})
Custom Renderer
Section titled “Custom Renderer”To customize the HTML output, extend ReactRenderer and override renderToString:
// src/renderer.tsximport { ReactRenderer, type RenderData } from '@nano_kit/react-ssr/renderer'import { routes, pages } from './index.js'
class AppRenderer extends ReactRenderer { renderToString(data: RenderData) { // call the default implementation or build your own HTML document return super.renderToString(data) }}
export const renderer = new AppRenderer({ base: import.meta.env.BASE_URL, manifestPath: import.meta.env.MANIFEST, routes, pages})Then point the plugin to your custom renderer file:
// vite.config.jsssr({ index: 'src/index.ts', renderer: 'src/renderer.tsx'})Custom Client
Section titled “Custom Client”To customize client-side hydration, provide your own client entry:
// src/client.tsximport { hydrateRoot } from 'react-dom/client'import { InjectionContextProvider } from '@nano_kit/react'import { App } from '@nano_kit/react-router'import { ROOT_ID, ready } from '@nano_kit/react-ssr/client'import { routes, pages } from './index.js'
ready({ routes, pages }).then((context) => { hydrateRoot( document.getElementById(ROOT_ID)!, <InjectionContextProvider context={context}> <App /> </InjectionContextProvider> )})Then point the plugin to your client file:
// vite.config.jsssr({ index: 'src/index.ts', client: 'src/client.tsx'})See Also
Section titled “See Also”- SSR — Getting Started — how the base SSR package works
- Router SSR — how to add
Stores$andHead$to page modules - React Router —
App,browserNavigation,router, and other React router APIs