---
# System prepended metadata

title: 'Angular Hydration and Incremental Hydration '

---

# Angular Hydration — Study Notes

> **Source:** [Angular Hydration Guide](https://angular.dev/guide/hydration#what-is-hydration)

## 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](https://angular.dev/guide/incremental-hydration)

> "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

```typescript
// 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

```html
<!-- 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

```html
<!-- 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

- https://angular.dev/guide/templates/defer
- https://angular.dev/guide/incremental-hydration
- https://github.com/angular/angular/issues/54797
- https://angular.dev/guide/routing/rendering-strategies#making-ssr-ssg-interactive-with-hydration