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({}))],
});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.