In-depth Guides
Server-side & hybrid-rendering

Incremental Hydration

Tip: Incremental hydration is currently in developer preview.

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 use incremental hydration?

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.

How do you enable incremental hydration in Angular?

You can enable incremental hydration for applications that already use server-side rendering (SSR) with hydration. Follow the Angular SSR Guide to enable server-side rendering and the Angular Hydration Guide to enable hydration first.

Enable incremental hydration by adding the withIncrementalHydration() function to the provideClientHydration provider.

      
import {  bootstrapApplication,  provideClientHydration,  withIncrementalHydration,} from '@angular/platform-browser';...bootstrapApplication(AppComponent, {  providers: [provideClientHydration(withIncrementalHydration())]});

Incremental Hydration depends on and enables event replay automatically. If you already have withEventReplay() in your list, you can safely remove it after enabling incremental hydration.

How does incremental hydration work?

Incremental hydration builds on top of full-application hydration, deferrable views, and event replay. With incremental hydration, you can add additional triggers to @defer blocks that define incremental hydration boundaries. 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. Any browser events, specifically those that match listeners registered in your component, that are triggered by the user prior to hydration are queued up and replayed once the hydration process is complete.

Controlling hydration of content with triggers

You can specify hydrate triggers that control when Angular loads and hydrates deferred content. These are additional triggers that can be used alongside regular @defer triggers.

Each @defer block may have multiple hydrate event triggers, separated with a semicolon (;). Angular triggers hydration when any of the triggers fire.

There are three types of hydrate triggers: hydrate on, hydrate when, and hydrate never.

hydrate on

hydrate on specifies a condition for when hydration is triggered for the @defer block.

The available triggers are as follows:

Trigger Description
hydrate on idle Triggers when the browser is idle.
hydrate on viewport Triggers when specified content enters the viewport
hydrate on interaction Triggers when the user interacts with specified element
hydrate on hover Triggers when the mouse hovers over specified area
hydrate on immediate Triggers immediately after non-deferred content has finished rendering
hydrate on timer Triggers after a specific duration

hydrate on idle

The hydrate on idle trigger loads the deferrable view's dependencies and hydrates the content once the browser has reached an idle state, based on requestIdleCallback.

      
@defer (hydrate on idle) {  <large-cmp />} @placeholder {  <div>Large component placeholder</div>}

hydrate on viewport

The hydrate on viewport trigger loads the deferrable view's dependencies and hydrates the corresponding page of the app when the specified content enters the viewport using the Intersection Observer API.

      
@defer (hydrate on viewport) {  <large-cmp />} @placeholder {  <div>Large component placeholder</div>}

hydrate on interaction

The hydrate on interaction trigger loads the deferrable view's dependencies and hydrates the content when the user interacts with the specified element through click or keydown events.

      
@defer (hydrate on interaction) {  <large-cmp />} @placeholder {  <div>Large component placeholder</div>}

hydrate on hover

The hydrate on hover trigger loads the deferrable view's dependencies and hydrates the content when the mouse has hovered over the triggered area through the mouseover and focusin events.

      
@defer (hydrate on hover) {  <large-cmp />} @placeholder {  <div>Large component placeholder</div>}

hydrate on immediate

The hydrate on immediate trigger loads the deferrable view's dependencies and hydrates the content immediately. This means that the deferred block loads as soon as all other non-deferred content has finished rendering.

      
@defer (hydrate on immediate) {  <large-cmp />} @placeholder {  <div>Large component placeholder</div>}

hydrate on timer

The hydrate on timer trigger loads the deferrable view's dependencies and hydrates the content after a specified duration.

      
@defer (hydrate on timer(500ms)) {  <large-cmp />} @placeholder {  <div>Large component placeholder</div>}

The duration parameter must be specified in milliseconds (ms) or seconds (s).

hydrate when

The hydrate when trigger accepts a custom conditional expression and loads the deferrable view's dependencies and hydrates the content when the condition becomes truthy.

      
@defer (hydrate when condition) {  <large-cmp />} @placeholder {  <div>Large component placeholder</div>}

Note: hydrate when conditions only trigger when they are the top-most dehydrated @defer block. The condition provided for the trigger is specified in the parent component, which needs to exist before it can be triggered. If the parent block is dehydrated, that expression will not yet be resolvable by Angular.

hydrate never

The hydrate never allows users to specify that the content in the defer block should remain dehydrated indefinitely, effectively becoming static content. Note that this applies to the initial render only. During a subsequent client-side render, a @defer block with hydrate never would still fetch dependencies, as hydration only applies to initial load of server-side rendered content. In the example below, subsequent client-side renders would load the @defer block dependencies on viewport.

      
@defer (on viewport; hydrate never) {  <large-cmp />} @placeholder {  <div>Large component placeholder</div>}

Note: Using hydrate never prevents hydration of the entire nested subtree of a given @defer block. No other hydrate triggers fire for content nested underneath that block.

Hydrate triggers alongside regular triggers

Hydrate triggers are additional triggers that are used alongside regular triggers on a @defer block. Hydration is an initial load optimization, and that means hydrate triggers only apply to that initial load. Any subsequent client side render will still use the regular trigger.

      
@defer (on idle; hydrate on interaction) {  <example-cmp />} @placeholder{  <div>Example Placeholder</div>}

In this example, on the initial load, the hydrate on interaction applies. Hydration will be triggered on interaction with the <example-cmp /> component. On any subsequent page load that is client-side rendered, for example when a user clicks a routerLink that loads a page with this component, the on idle will apply.

How does incremental hydration work with nested @defer blocks?

Angular's component and dependency system is hierarchical, which means hydrating any component requires all of its parents also be hydrated. So if hydration is triggered for a child @defer block of a nested set of dehydrated @defer blocks, hydration is triggered from the top-most dehydrated @defer block down to the triggered child and fire in that order.

      
@defer (hydrate on interaction) {  <parent-block-cmp />  @defer (hydrate on hover) {    <child-block-cmp />  } @placeholder {    <div>Child placeholder</div>  }} @placeholder{  <div>Parent Placeholder</div>}

In the above example, hovering over the nested @defer block triggers hydration. The parent @defer block with the <parent-block-cmp /> hydrates first, then the child @defer block with <child-block-cmp /> hydrates after.

Constraints

Incremental hydration has the same constraints as full-application hydration, including limits on direct DOM manipulation and requiring valid HTML structure. Visit the Hydration guide constraints section for more details.

Do I still need to specify @placeholder blocks?

Yes. @placeholder block content is not used for incremental hydration, but a @placeholder is still necessary for subsequent client-side rendering cases. If your content was not on the route that was part of the initial load, then any navigation to the route that has your @defer block content renders like a regular @defer block. So the @placeholder is rendered in those client-side rendering cases.