BACK TO BLOG
ENGINEERING 08 SEP 2026 · 8 MIN READ

Angular Hydration and Incremental Hydration

Ali Gamal Ali

Lead Front-End Developer · Luftborn


// QUICK REACTIONS

Enjoyed this article? Leave a signal without writing a comment.

Angular Hydration — Study Notes

Source: Angular Hydration Guide

What is Hydration?

“Hydration is the process that restores the server-side rendered application on the client. This includes things like reusing the server rendered DOM structures, persisting the application state, transferring application data that was retrieved already by the server, and other processes.”

What it does

  • The server renders the entire page as HTML
  • The browser displays this HTML immediately (fast first paint)
  • Angular then boots up on the client and reuses the existing DOM nodes instead of destroying and recreating them
  • Event listeners get attached, the app becomes interactive

Why it matters

“Without hydration enabled, server-side rendered Angular applications will destroy and re-render the application’s DOM, which may result in a visible UI flicker. This re-rendering can negatively impact Core Web Vitals like LCP and cause a layout shift. Enabling hydration allows the existing DOM to be re-used and prevents a flicker.”

The problem with full hydration

Full hydration is all-or-nothing. When Angular hydrates, it loads all JavaScript for the entire page at once and hydrates every component. For a large page, this means:

  • Large initial JS bundle (everything loads upfront)
  • The entire component tree must hydrate before anything is interactive
  • You can’t use @defer above the fold because it only renders the @placeholder on the server, causing a layout shift when the real content replaces it

Incremental Hydration

Source: Angular Incremental Hydration Guide

“Incremental hydration is an advanced type of hydration that can leave sections of your application dehydrated and incrementally trigger hydration of those sections as they are needed.”

Why it was introduced

“Incremental hydration is a performance improvement that builds on top of full application hydration. It can produce smaller initial bundles while still providing an end-user experience that is comparable to a full application hydration experience. Smaller bundles improve initial load times, reducing First Input Delay (FID) and Cumulative Layout Shift (CLS).”

“Incremental hydration also lets you use deferrable views (@defer) for content that may not have been deferrable before. Specifically, you can now use deferrable views for content that is above the fold. Prior to incremental hydration, putting a @defer block above the fold would result in placeholder content rendering and then being replaced by the @defer block’s main template content. This would result in a layout shift. Incremental hydration means the main template of the @defer block will render with no layout shift on hydration.”

The above‑the‑fold area is the part of a web page that the user can see immediately when it first loads, before any scrolling. Anything you have to scroll down to see is called below the fold.

How it works

“Adding a hydrate trigger to a defer block tells Angular that it should load that defer block’s dependencies during server-side rendering and render the main template rather than the @placeholder. When client-side rendering, the dependencies are still deferred, and the defer block content stays dehydrated until its hydrate trigger fires. That trigger tells the defer block to fetch its dependencies and hydrate the content.”


Side-by-Side Comparison

Feature Full Hydration Incremental Hydration
When JS loads All at once on page load On-demand per @defer block
When components hydrate All at once Only when their hydrate trigger fires
Initial bundle size Entire app Only non-deferred parts
@defer on server Renders @placeholder only Renders main content
Above-the-fold @defer Causes layout shift No layout shift
User events before hydration Lost (unless withEventReplay() added) Automatically queued and replayed
Enable with provideClientHydration() provideClientHydration(withIncrementalHydration())

Visual Timeline

Full Hydration:

Server HTML arrives → User sees content → ALL JS loads → ALL components hydrate → App interactive
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                          Everything loads at once (big bundle)

Incremental Hydration:

Server HTML arrives → User sees content → Core JS loads → App partially interactive
                                                ↓
                          Section A: hydrates on idle ──→ Interactive
                          Section B: hydrates on viewport ──→ Interactive (when scrolled to)
                          Section C: hydrates on interaction ──→ Interactive (when clicked)
                          Section D: hydrate never ──→ Stays static (0 JS cost)

In short — full hydration was the first step to avoid the “destroy and rebuild DOM” problem. Incremental hydration was introduced to solve the “load everything at once” problem, giving you fine-grained control over when each section of your page becomes interactive while still rendering full content on the server for SEO.


Angular Incremental Hydration & @defer

By default, @defer blocks do NOT render their main content on the server. During SSR/SSG, Angular renders only the @placeholder (or nothing if no placeholder exists). This means search engine crawlers won’t see the deferred content, which is bad for SEO.

The Solution: Incremental Hydration with hydrate Triggers

Angular’s Incremental Hydration feature solves this. When you add a hydrate trigger to a @defer block, Angular:

  1. Server-side: Loads the deferred block’s dependencies and renders the full main content (not the placeholder)
  2. Client-side: Keeps the dependencies deferred until the trigger fires, then hydrates the content
  3. User interactions before hydration are queued and replayed after hydration completes

Step 1 — Enable Incremental Hydration

// app.config.ts
import { provideClientHydration, withIncrementalHydration } from '@angular/platform-browser';

export const appConfig: ApplicationConfig = {
  providers: [
    provideClientHydration(withIncrementalHydration()),
    // ...
  ],
};

This automatically enables event replay as well.


Step 2 — Add hydrate Triggers to @defer Blocks

<!-- 1. SEO-critical content: rendered on server, hydrated when visible -->
@defer (hydrate on viewport) {
  <app-product-details [product]="product" />
} @placeholder {
  <div class="skeleton">Loading product details...</div>
}

<!-- 2. SEO-critical but below-the-fold: rendered on server, hydrated on idle -->
@defer (hydrate on idle) {
  <app-related-products [categoryId]="categoryId" />
} @placeholder {
  <div class="skeleton">Loading related products...</div>
}

<!-- 3. SEO-IMPORTANT but static → hydrate never (renders on server, 0 JS cost) -->
@defer (hydrate never) {
  <app-seo-footer />
}

<!-- 4. NOT SEO-RELEVANT → regular @defer (no server render) -->
@defer (on interaction) {
  <app-user-comments />
} @placeholder {
  <button>Load Comments</button>
}

Available hydrate Triggers

Trigger Behavior SEO Use Case
hydrate on viewport Hydrates when element enters viewport Below-the-fold SEO content
hydrate on idle Hydrates when browser is idle Important but non-urgent content
hydrate on interaction Hydrates on click/keydown Interactive SEO content
hydrate on hover Hydrates on mouseover/focusin Content revealed on hover
hydrate on immediate Hydrates right after load Critical above-the-fold content
hydrate on timer(ms) Hydrates after delay Timed content loading
hydrate when condition Hydrates when expression is truthy Conditional content
hydrate never Never hydrates (stays static) Purely static SEO content (footer, etc.)

Key Differences: @defer vs @defer with hydrate

Regular @defer @defer with hydrate trigger
Server renders @placeholder only Full main content
SEO crawlers see Placeholder text Actual content
Client JS loaded When trigger fires When hydrate trigger fires
Layout shifts Possible (placeholder → content) Minimal (content already there)

Strategy: Choosing the Right Approach per Content Type

<!-- 1. SEO-CRITICAL, above the fold → hydrate on immediate -->
@defer (hydrate on immediate) {
  <app-hero-section />
}

<!-- 2. SEO-CRITICAL, below the fold → hydrate on idle -->
@defer (hydrate on idle) {
  <app-blog-content [post]="post" />
}

<!-- 3. SEO-IMPORTANT but static → hydrate never -->
@defer (hydrate never) {
  <app-seo-footer />
}

<!-- 4. NOT SEO-RELEVANT → regular @defer (no server render) -->
@defer (on interaction) {
  <app-user-comments />
} @placeholder {
  <button>Load Comments</button>
}

Summary

  • Without hydrate triggers → @defer renders only @placeholder on the server (bad for SEO)
  • With hydrate triggers → @defer renders the full content on the server (good for SEO), while still deferring the JavaScript hydration on the client
  • Use hydrate never for static SEO content that doesn’t need interactivity — zero JS cost
  • Use regular @defer (no hydrate) for non-SEO content that only needs client-side rendering

Sources

// QUICK REACTIONS

Enjoyed this article? Leave a signal without writing a comment.


// DISCUSSION

Reader Comments (0)

Register or log in to leave a comment.

// NO COMMENTS YET