Skip to content
Built with

Custom SSR ​

Build & Deploy

Use custom SSR when you want to choose your own router, data loading, HTML shell and hydration. If you'd rather the SDK handle these, use Pages routing.

On this pageCreate a custom SSR app · Add the required entry files · Deploy it

Create the app ​

This step creates a root component. The React example renders a page from the request path.

tsx
// src/App.tsx
export default function App({ url }: { url: string }) {
  return url === '/about' ? <main>About</main> : <main>Home</main>;
}

Required entries ​

This step adds the two entry files.

  • src/main.ssr.ts or src/main.ssr.tsx
  • src/main.client.ts or src/main.client.tsx

You need exactly one server entry and one client entry.

Render API ​

This step renders HTML on the server. Wrap the renderer in defineRender() and return an HTML response. Put assetTags.css and assetTags.preloads in <head>. Put assetTags.body before </body>.

tsx
// src/main.ssr.tsx
import { renderToString } from 'react-dom/server';
import { defineRender } from 'void';
import App from './App';

export default defineRender(async (c, assetTags) => {
  const url = new URL(c.req.raw.url);
  const html = renderToString(<App url={url.pathname} />);
  return new Response(
    `<!doctype html>
<html>
  <head>${assetTags.css}${assetTags.preloads}</head>
  <body>
    <div id="root">${html}</div>
    ${assetTags.body}
  </body>
</html>`,
    { headers: { 'content-type': 'text/html; charset=utf-8' } },
  );
});

A named render(c, assetTags) export with the same signature also works. See defineRender for the types.

Hydrate in the browser ​

This step hydrates the same component in the client entry.

tsx
// src/main.client.tsx
import { hydrateRoot } from 'react-dom/client';
import App from './App';

hydrateRoot(document.getElementById('root')!, <App url={window.location.pathname} />);

Caching ​

See Revalidation for stale-while-revalidate caching of SSR pages.

API routes and static files are served first. Other non-API requests go to render(c, assetTags).

Next steps ​

implemented.stream AI