Files
blog/apps/frontend/src/entry-server.tsx
T
2026-09-06 23:29:00 -04:00

43 lines
1.5 KiB
TypeScript

import { renderToString } from "react-dom/server";
import { StaticRouter } from "react-router-dom";
import { AuthProvider } from "./providers/AuthProvider";
import { SsrDataProvider } from "./ssr/context";
import { matchSsrRoute, type ApiFetch } from "./ssr/routes";
import type { SsrData, SsrHead } from "./ssr/types";
import App from "./app";
type RenderResult =
/** Not a server-rendered route - serve the client-only shell. */
| { kind: "spa" }
/** Server-rendered route whose content is missing - shell, with a 404. */
| { kind: "not-found" }
| { kind: "ssr"; html: string; data: SsrData; head: SsrHead };
async function render(url: string, api: ApiFetch): Promise<RenderResult> {
const pathname = new URL(url, "http://ssr.invalid").pathname;
const matched = matchSsrRoute(pathname);
if (!matched) return { kind: "spa" };
const loaded = await matched.route.load(api, matched.params);
if (!loaded) return { kind: "not-found" };
// The visitor's session is deliberately not resolved here: auth-dependent
// chrome renders logged-out on the server and AuthProvider fills it in after
// hydration, which keeps these pages cacheable and hydration-safe.
const html = renderToString(
<SsrDataProvider data={loaded.data}>
<AuthProvider>
<StaticRouter location={url}>
<App />
</StaticRouter>
</AuthProvider>
</SsrDataProvider>,
);
return { kind: "ssr", html, data: loaded.data, head: loaded.head };
}
export { render };
export { defaultHead } from "./ssr/routes";
export type { RenderResult };