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.
2. Installation & Architecture
Since Angular 17+, installation is a one-liner. This command automatically updates your app and creates three new, important files.
ng add @angular/ssrWhat 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.tsin the SSR context). - app.routes.server.ts: The configuration for which routes are built live (SSR) or statically (Prerender).
// 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 DOM3. 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!
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 (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.
# 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.