SSR / RSC
Server-rendering constraints, use client boundaries and verification apps.
Hard constraints
- Server render must not throw or touch
window,documentorlocalStorage. - Initial HTML must be complete; critical content cannot wait for the client.
- Hydration must not warn; server and client first frames must match.
packages/ui/src/ssr.test.tsx uses renderToStaticMarkup in Node to render primitives, ScreenPanel and PlayerInventory. Any browser API at module scope or during render fails the test.
use client boundaries
- Base UI components opt into
"use client"in the component or by the caller when interaction is needed. useHasHoverreturnsfalseon the server and readsmatchMediain an effect; hover is a desktop enhancement.- Do not branch markup on
typeof window !== "undefined"; it produces first-frame mismatches. Put the difference in an effect or CSS.
Two-framework verification
pnpm --filter @tennantite/next-app dev
pnpm --filter @tennantite/tanstack-app dev
Next.js App Router runs on port 3100 and TanStack Start on 3200. Both render the same controls to catch framework-level prerender and hydration differences.
Automated smoke test
pnpm --filter @tennantite/ui test
The Vitest SSR test uses the Node environment, not jsdom. It proves “the server can render”, not browser interaction; interaction and accessibility are covered by the jsdom tests.
This site
This site is statically generated by Astro at build time. Documentation pages load no preview/demo React chunk; only the idle-hydrated search island and a small native copy script. Component and Block pages hydrate previews on demand.