Ch. 4 · Angular

Angular SSR and Hydration Consistency

Server-render Angular apps, hydrate without mismatches, and avoid the DOM and timing assumptions that break rendering.

~2 min readadvancedupdated Oct 5, 2026

Server-side rendering produces HTML on the server, and hydration attaches Angular to that HTML in the browser instead of rebuilding the DOM. When the server and client render different content, hydration cannot match and Angular discards and re-renders, which shows a flash and sometimes logs a mismatch error.

Before you start

You should be comfortable with Angular components and change detection. This article covers rendering consistency; it assumes a server-rendered setup.

Step-by-step walkthrough

Step 1: Keep rendering deterministic

The same inputs must produce the same HTML on both sides. Values such as Date.now(), Math.random() and locale formatting differ between the server and the browser, so read them in a browser-only hook or after hydration, not during render.

Step 2: Guard browser-only APIs

window, document and localStorage do not exist on the server. Access them through isPlatformBrowser or after hydration, and use Angular’s DOCUMENT token rather than a global, so the same code runs in both environments.

Step 3: Enable hydration and transfer state

provideClientHydration() turns on hydration and reuses the server’s data instead of refetching. The HTTP transfer cache passes server responses to the client, so the first render matches and the app does not re-request data it already has.

Worked scenario

The bootstrap enables hydration and the HTTP transfer cache.

import { bootstrapApplication } from '@angular/platform-browser';
import { provideClientHydration, withHttpTransferCacheOptions } from '@angular/platform-browser';

bootstrapApplication(AppComponent, {
  providers: [provideClientHydration(withHttpTransferCacheOptions({}))],
});
TypeScript

Walk through the example

Hydration reuses the server HTML, and the transfer cache replays the server’s HTTP responses so the client’s initial render produces the same output. Without the cache, the client would refetch and could render a loading state the server did not, causing a mismatch. A provideClientHydration setup plus deterministic rendering is what makes hydration seamless.

Common mistake

Reading the current time or a random id in a template, which differs across environments and forces a mismatch. Another is touching localStorage during render on the server, which throws; read it in a browser-only lifecycle hook instead.

Verify the behavior

Check the browser console for hydration mismatch warnings (for example NG0500) and confirm there are none. Compare the server HTML and the first client render for the same route. Disable the transfer cache and observe the extra requests it otherwise prevents.

Interview exercise

Why can formatting a date with the local timezone cause a hydration mismatch?

Answer and reasoning

The server runs in one timezone and the browser in another, so the same timestamp formats to different strings, producing different HTML on each side. Hydration then finds content it cannot match. Fix it by formatting in a fixed timezone on the server, using a locale that matches, or deferring the browser-specific formatting until after hydration.

Continue learning

Compare rendering concerns in Angular lazy route boundaries and view query timing. Read the Angular server-side rendering guide and try the Angular interview questions.

More in Angular

read ✓Angular · mid

Angular Defer Blocks and Lazy Rendering

Load template regions on demand with @defer, choose a trigger, and use placeholders without delaying critical content.

~2 min readread →
read ✓Angular · hard

Angular Global Error Handling

Catch uncaught errors with a custom ErrorHandler, distinguish client from HTTP errors, and surface safe messages to users.

~2 min readread →
read ✓Angular · mid

Angular Host Directives and Composition

Attach behavior to elements with attribute directives, bind to the host with the host property, and compose directives with hostDirectives.

~2 min readread →
esc