Logo

Performance & SEO

Server Side Rendering (SSR)

From an empty box to a fully furnished setup. SSR delivers ready-made HTML, makes Google happy, and makes your app feel faster.

By default, Angular is like an IKEA package (CSR): The browser gets an empty box
(<app-root></app-root>) and an instruction manual (JavaScript). The browser has to assemble the page before the user sees anything.

The problem: Google bots, WhatsApp, and LinkedIn hate building furniture.
They often only see the empty shell.
The solution (SSR): A Node.js server assembles the furniture and sends the finished sofa to the browser. The user sees content immediately (First Contentful Paint).

1. CSR vs. SSR Comparison

Press "Reload". Notice how long it takes on the left for the text to appear (loading spinner). On the right, the text is there immediately. Switch to "Source Code": On the left, you see empty HTML; on the right, real content.

Standard Angular (CSR)
Loading JavaScript Bundle... (2.5MB)
Angular SSR + Hydration

Welcome!

This HTML came ready from the server.

💧 Hydrating... (JS attaching)

2. Installation & Architecture

Since Angular 17+, installation is a one-liner. This command automatically updates your app and creates three new, important files.

Terminal
ng add @angular/ssr

What happens in the background?

  • server.ts: An Express server (Node.js). It acts as the bridge between the web and your Angular app.
  • main.server.ts: The entry point for the server (replaces main.ts in the SSR context).
  • app.routes.server.ts: The configuration for which routes are built live (SSR) or statically (Prerender).
The New Files
// server.ts
// An Express server (Node.js) that handles requests.
const app = express();
// ...renders Angular and sends back HTML.

// main.server.ts
// The entry point for the app on the server (instead of main.ts).
export default bootstrap; // Starts the app without the browser DOM

3. The Deadly Trap: Window & DOM

This is where 90% of developers fail. The Node.js server has no screen. It doesn't know window, document, localStorage, or canvas.

If you start animations (GSAP) or Canvas while the server is rendering, the process crashes. The solution is the "Golden Rule": Always check the platform!

Safe SSR Pattern
import { Component, inject, PLATFORM_ID, AfterViewInit } from '@angular/core';
import { isPlatformBrowser } from '@angular/common'; // <--- IMPORTANT

export class AnimationComponent implements AfterViewInit {
  // 1. Inject platform ID
  private platformId = inject(PLATFORM_ID);

  ngAfterViewInit() {
    // 2. CHECK: Are we in the browser?
    // The server does not know 'window', 'document', 'gsap', or 'canvas'.
    // If we don't check, the server will crash.
    if (!isPlatformBrowser(this.platformId)) return;

    // --- SAFE ZONE ---
    // Here we can do everything:
    gsap.to('.box', { x: 100 });
    this.canvas.getContext('2d');
    console.log(window.innerWidth);
  }
}
💡

Use this pattern (isPlatformBrowser) wherever you have GSAP, Canvas, EventListeners, or window access.

4. Routing & Prerendering Errors

During the build (npm run build), Angular tries to prepare every page. This works great for /home.
But for dynamic routes like /profile/:username, the build fails: getPrerenderParams is missing.

The solution: You need to tell Angular in app.routes.server.ts which routes should be calculated on the server (RenderMode.Server).

app.routes.server.ts
// app.routes.server.ts (New in Angular 19)
import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  // DYNAMIC (SSR):
  // Routes with parameters (:username, :token) must be rendered live.
  {
    path: 'profile/:username',
    renderMode: RenderMode.Server
  },

  // STATIC (SSG):
  // Everything else is built once during the build process (extremely fast).
  {
    path: '**',
    renderMode: RenderMode.Prerender
  }
];

5. Testing Like a Pro

How do you know if your app is truly SSR-safe? Here are 3 practical methods.

Quality Assurance
# Method 1: Terminal Check (Live)
ng serve
# Reload the page (F5). If red errors appear in the VS Code terminal
# (ReferenceError: window is not defined), it is not safe.

# Method 2: No-JS Check (The ultimate test)
# Chrome DevTools -> Ctrl+Shift+P -> "Disable JavaScript"
# Reload the page. Is the content visible? ✅

# Method 3: Build Check (Strict)
npm run build
# The build is stricter than 'ng serve'. It immediately finds routing issues.
  • The Terminal Check: The most important one. If errors appear here while you're browsing, your code isn't clean.
  • The No-JS Check: Shows you what the Google bot sees. If the page stays blank, SSR isn't working.