2. Static Prerendering with Hydration, Not Runtime SSR

Status

Accepted — 2026-03-06 (pre-launch)

Context

jjk.engineer is a content-heavy blog with a fixed, buildable route manifest: posts, pages, tag indexes, the series index, and a handful of tools routes. The dynamic surface area (admin dashboards, login) is small and does not need to be crawlable or fast on first paint. Angular supports both full runtime SSR (Node server renders on request) and prerender-at-build output; the default developer instinct is to reach for SSR because it sounds more capable.

Decision

Build-time prerender with client hydration, not runtime SSR.

  • angular.json sets prerender.discoverRoutes: false and reads routes from routes.txt, which the content pipeline generates at build time from the known post/page/tag set.
  • app.routes.server.ts declares RenderMode.Prerender as the wildcard default and explicitly downgrades /login, /admin, /admin/inbox, and /admin/ratings to RenderMode.Client.
  • main.server.ts and app.config.server.ts exist only to serve the build-time prerender step. There is no Node server in the serving path; Firebase Hosting serves static HTML plus hydrated Angular bundles.

Consequences

  • Zero runtime server cost for the public blog. Hosting is pure static + CDN.
  • Any new public route must be addable to the build-time route manifest. Anything with per-request dynamic content (personalized SSR output) is not supported without changing this posture.
  • Admin routes intentionally render client-only; they load behind auth and are not indexable. This is the correct trade for an admin surface with no SEO requirement.
  • Build time scales linearly with route count. At current post volume this is not a concern, but a blog with tens of thousands of routes would eventually hit friction.
  • Reversing this decision later (adopting runtime SSR) is possible — the @angular/ssr wiring is already present — but would add a Node serving target, a cold-start profile, and an operational surface that currently does not exist.