Server-Side Rendering

HTML on the First Hit, JS After

Server-Side Rendering

`ng add @angular/ssr` wires up an Express-based render server so the first request returns finished HTML while the JS bundle loads in the background.

4 min read Level 3/5 #angular#ssr#performance
What you'll learn
  • Add SSR with a single ng add command
  • Understand main.server.ts vs main.ts
  • Know what breaks under SSR (window, document, localStorage)

Client-only Angular has a problem: the first byte from the server is a near-empty HTML shell, and users stare at a blank screen until the JS bundle arrives, parses, and renders. Server-side rendering fixes that.

Add SSR

ng add @angular/ssr

The schematic generates server.ts, main.server.ts, and updates angular.json with a server build target. Two entry points now exist:

  • main.ts — boots Angular in the browser
  • main.server.ts — boots Angular in Node so it can render to a string

Build and Run

ng build
node dist/my-app/server/server.mjs

A request to / runs your component tree through renderApplication, returns HTML, and the static asset middleware serves the JS/CSS afterwards.

What Breaks Under SSR

Node doesn’t have window, document, or localStorage. Code that touches them throws when rendered on the server.

import { Component, PLATFORM_ID, inject, afterNextRender } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Component({ selector: 'app-x', standalone: true, template: `` })
export class XComponent {
  private isBrowser = isPlatformBrowser(inject(PLATFORM_ID));

  constructor() {
    if (this.isBrowser) {
      // safe to touch window here
    }

    afterNextRender(() => {
      // even safer — runs once, on the browser only, after the first render
    });
  }
}

afterNextRender is the modern idiom for browser-only effects — measuring the DOM, third-party scripts, anything that needs a real browser.

Hydration →