Learn Angular - Server-side Rendering & Universal
Episode 21 of 24

Learn Angular - Server-side Rendering & Universal

This episode covers server-side rendering and Angular Universal: the Universal basics, the difference between prerendering and SSR, SEO and initial load improvements, and hydration and the interaction between server and client.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

A standard Angular application is rendered in the browser, so crawlers and users see a blank screen while JavaScript loads. Server-side rendering moves the HTML rendering work to the server, producing a page that's immediately readable.

Episode 21 covers the Angular Universal basics, the difference between prerendering and server-side rendering, SEO and initial load improvements, and hydration and the interaction between server and client. You'll understand when SSR is needed and how to configure it.

The key concept is one application, two execution environments. The browser and the server both run the same components, but with different responsibilities.

Angular Universal Basics

What Universal Adds

Universal is Angular's way to render an application on the server. Setting it up is a single command:

Add Angular Universal
ng add @angular/nguniversal

This command adds server.ts, a server builder in angular.json, and the dev:ssr, build:ssr, and serve:ssr scripts. The main.server.ts file becomes the server entry point, which uses renderApplication or renderModule.

JSServer entry point
import { renderApplication } from "@angular/platform-server";
import { AppComponent } from "./app/app.component";
import { appConfig } from "./app/app.config";
 
const bootstrap = () => import("./main").then(() => AppComponent);
 
renderApplication(AppComponent, {
  providers: appConfig.providers,
});

Running SSR in Development

Development mode runs a Node server with automatic rebuilds:

SSR dev server
npm run dev:ssr

Every file change is reflected immediately, and you can inspect the HTML sent by the server via the browser's view-source.

Prerendering vs Server-side Rendering

Prerendering for Static Pages

Prerendering turns every route into an HTML file at build time. Run the build with prerender:

Build with prerender
ng build --prerender

The result is a static folder that any CDN can serve. It suits pages whose content is the same for all users.

SSR for Dynamic Pages

Server-side rendering produces HTML on every request, so page content can differ per user:

Build and run SSR
ng build
npm run serve:ssr

serve:ssr runs the Node server from the server.ts file. Choose prerender if the page is nearly static, and SSR if the page depends on user data, session, or cookies.

SEO Improvements and Initial Load

Meta Tags and Open Graph

A server-rendered page can inject meta tags that crawlers and social media read. Angular provides Meta and Title from @angular/platform-browser:

JSSetting meta dynamically
import { Injectable } from "@angular/core";
import { Meta, Title } from "@angular/platform-browser";
 
@Injectable({ providedIn: "root" })
export class SeoService {
  constructor(private title: Title, private meta: Meta) {}
 
  update(title: string, description: string): void {
    this.title.setTitle(title);
    this.meta.updateTag({ name: "description", content: description });
  }
}

With SSR, these meta tags are already in the HTML the crawler receives, so Google doesn't have to wait for JavaScript.

Faster Initial Load

Users see server-rendered content faster, and the browser does less work for the first paint. Metrics like Largest Contentful Paint and time to interactive usually improve significantly.

Hydration and Server-Client Interaction

Enabling Hydration

Hydration is the process of connecting the Angular application to HTML that's already been rendered on the server. Enable it with provideClientHydration:

JSEnable hydration
import { provideClientHydration } from "@angular/platform-browser";
import { ApplicationConfig } from "@angular/core";
 
export const appConfig: ApplicationConfig = {
  providers: [provideClientHydration()],
};

Once hydration is active, Angular recognizes the existing DOM, attaches event listeners, and doesn't rebuild the page from scratch. The result is no screen flicker and interactions that work right away.

TransferState for Data

A server that fetches data early can share the result with the client via TransferState, so the client doesn't fetch the same data twice:

JSSharing data from the server
import { TransferState, makeStateKey } from "@angular/core";
import { inject } from "@angular/core";
 
const CATEGORIES_KEY = makeStateKey("categories");
 
// di server: tulis data
// transferState.set(CATEGORIES_KEY, categories);
 
// di client: baca data
// const categories = transferState.get(CATEGORIES_KEY, null);

This prevents the flash of empty content and double fetching, two common problems in SSR applications without TransferState.

Wrap Up

Key takeaways:

  • Universal lets one application be rendered on both the server and the browser.
  • Prerendering produces static files; SSR renders per request.
  • Meta tags and titles can be injected through an SEO service.
  • SSR improves SEO and speeds up the initial load.
  • Hydration connects the application to already-rendered HTML.
  • TransferState shares data from the server to the client without double fetching.

In the next episode, episode 22, we'll cover observability and monitoring — logging frontend errors and performance metrics, monitoring user interactions, error reporting with Sentry or LogRocket, and production support and incident handling.

Learn Angular - Server-side Rendering & Universal | Learn Angular