angular/angular
Server-side rendering
Rendering Angular templates in Node.js, sending the resulting HTML as the server response, and (with hydration enabled) reusing that HTML at client boot. Spans @angular/platform-server, @angular/ssr, and @angular/build (in the CLI repo).
What it covers
Angular SSR supports four delivery modes:
- SSR (server-side rendering at request time). A request hits a Node handler that renders fresh HTML and returns it.
- SSG (static site generation). At build time the framework renders all known routes and writes them as static files.
- Prerendering. A subset of SSG: a list of routes is prerendered while everything else falls back to client rendering.
- Hybrid (SSR + hydration). The default. Server HTML is reused at client boot via hydration annotations.
High-level pipeline
graph LR Request["HTTP request"] --> Handler["Server handler<br/>(Express, Hono, Vercel, ...)"] Handler --> Render["renderApplication()"] Render --> Domino["DOM via Domino"] Domino --> Boot["bootstrapApplication on server"] Boot --> Tick["tick() until stable"] Tick --> Serialize["Serialize DOM + TransferState"] Serialize --> Response["HTML response"] Response --> Browser Browser --> Hydrate["provideClientHydration() walks DOM"] Hydrate --> Live["Live, interactive app"]
renderApplication lives in packages/platform-server/src/render.ts. It builds an Angular application against domino-backed DOM, runs change detection until the application reaches stability (ApplicationRef.isStable becomes true), then serializes the result.
Stability
A render isn't sent until the app is stable. Stability means no microtasks, macrotasks, or Pending Tasks are outstanding:
Pending Tasks(packages/core/src/pending_tasks.ts) — explicit "don't ship the response yet" tokens, used by HTTP and signals to indicate in-flight work.- Zone tasks (in zone mode) or scheduler emptiness (in zoneless mode).
If your component spawns long-running async work, use pendingTasks.add() to keep SSR waiting; otherwise the response will go out before the work finishes.
Hydration
The companion to SSR: instead of throwing away the server-rendered DOM, the client matches it against template execution and reuses each node. See systems/hydration for the full flow. Activated by provideClientHydration() on the client side.
platform-server emits the matching annotations (data attributes plus a <script> containing TransferState JSON) that hydration consumes.
TransferState
State that should not be re-fetched on the client:
HttpClientrequests made on the server are cached under synthetic keys; the clientHttpClientreads from the cache before issuing real requests.- Custom data can be moved with
TransferStatedirectly:transferState.set(key, value)on the server,transferState.get(key, default)on the client.
The <script> island inserted into the HTML is the transport.
SSR-specific lifecycle hooks
afterNextRender(fn, {phases: ['read' | 'mixedReadWrite' | 'write']})— schedule work for after the first client render. On the server, these hooks don't fire — they're explicitly client-only. Use them for DOM-touching code.- The
isPlatformServer(injector.get(PLATFORM_ID))andisPlatformBrowser(...)predicates from@angular/commonare still the official platform check.
Provider configuration
Server-side:
const html = await renderApplication(AppRoot, {
url: '/some-route',
document: '<!doctype html><html>…</html>',
providers: [
provideServerRendering(),
provideRouter(routes),
provideHttpClient(withFetch()),
],
});Client-side:
bootstrapApplication(AppRoot, {
providers: [
provideClientHydration(),
provideRouter(routes),
provideHttpClient(withFetch()),
],
});The mirroring is intentional: every server-side provider has a matching client-side provider.
Build-system integration
The CLI's @angular/build package ships the build adapter:
- Generates separate browser and server bundles.
- Includes a
server.tsentry that wires the renderer behind an Express handler by default. - For SSG / prerendering, runs the rendering loop at build time with a list of routes.
The @angular/ssr package wraps the renderer in opinionated AngularNodeAppEngine / AngularAppEngine adapters used by the generated server.ts.
Performance notes
- Domino is not zero-cost. Server renders are CPU-bound. Cache rendered HTML aggressively where possible.
- Avoid blocking work in
ngOnInitwhen SSR is in play. The application waits for stability; an HTTP request that takes 5s blocks the response by 5s. - Use
provideHttpClient(withFetch())for SSR — it integrates with response streaming and caching better than the XHR backend.
Integration points
@angular/platform-server— the renderer.@angular/corehydration — the client-side counterpart.@angular/router— receives the URL viaINITIAL_CONFIG.@angular/common/http— usesTransferStateto deduplicate requests across server/client boundary.@angular/service-worker— turns off during SSR; activates only on the client.- Build pipeline —
@angular/build(CLI repo).
Where to start when modifying
- A new SSR provider option: extend
packages/platform-server/src/init/. - A new hydration annotation: see
systems/hydration— the work spanscoreandplatform-server. - A
TransferStateenhancement: editpackages/core/src/transfer_state.tsand the matching server-side serializer.
The integration test at integration/platform-server-hydration/ is the canonical reproduction harness.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.