Incremental Hydration ๐ง
Incremental hydration is like having a smart loading system for your Angular app - instead of loading everything at once, it loads components precisely when theyโre needed. Think of it as upgrading from a gas-guzzling truck to a hybrid car that only uses fuel when necessary.
๐ฏ What is Incremental Hydration?
Section titled โ๐ฏ What is Incremental Hydration?โIncremental hydration is Angularโs game-changing approach to SSR that dramatically improves performance by hydrating components only when needed, rather than all at once. Itโs like having a smart waiter who brings you each course exactly when youโre ready for it.
Key Benefits:
- โก Faster LCP - Users see content immediately
- ๐ Better INP - Interactions respond instantly
- ๐ Stable CLS - No unexpected layout shifts
- ๐ง Smarter Loading - JavaScript loads only when needed
- ๐ฑ Mobile Optimized - Perfect for slower devices
๐ How It Works
Section titled โ๐ How It WorksโTraditional: All components hydrate immediately โ Slow, blocking Incremental: Components hydrate when needed โ Fast, responsive
- Critical content (header, main) โ Hydrates immediately
- Below-the-fold (footer, comments) โ Hydrates on viewport
- Interactive features (search, filters) โ Hydrates on interaction
- Heavy components (charts, analytics) โ Hydrates on idle
๐ ๏ธ Setting Up Incremental Hydration
Section titled โ๐ ๏ธ Setting Up Incremental HydrationโStep 1: Enable SSR (If Not Already Done)
Section titled โStep 1: Enable SSR (If Not Already Done)โ# Add SSR to existing project (built-in since Angular 17)ng add @angular/ssr
# Or create new project with SSRng new my-app --ssrStep 2: Enable Incremental Hydration
Section titled โStep 2: Enable Incremental HydrationโUpdate your app configuration:
import { ApplicationConfig } from '@angular/core';import { provideRouter } from '@angular/router';import { provideClientHydration, withIncrementalHydration } from '@angular/platform-browser';
export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), // Enable incremental hydration - that's it! ๐ provideClientHydration(withIncrementalHydration()), // Your other providers... ]};Thatโs literally it! Angular handles the complex stuff automatically.
๐ฎ Using @defer for Smart Loading
Section titled โ๐ฎ Using @defer for Smart LoadingโBasic @defer Syntax
Section titled โBasic @defer SyntaxโThe @defer block is your hydration control center:
@Component({ template: ` <div class="app"> <!-- Critical content loads immediately --> <header class="header"> <h1>My App</h1> <nav>...</nav> </header>
<!-- Defer heavy components --> @defer (on viewport) { <heavy-chart-component /> } @placeholder { <div class="chart-skeleton">Loading chart...</div> } @loading { <div class="spinner">โณ Loading...</div> } @error { <div class="error">Failed to load chart</div> } </div> `})export class AppComponent {}๐ฏ Hydration Triggers
Section titled โ๐ฏ Hydration Triggersโ1. hydrate on interaction - User Clicks/Taps
Section titled โ1. hydrate on interaction - User Clicks/Tapsโ@Component({ template: ` <!-- Hydrates only when user interacts --> @defer (hydrate on interaction) { <expensive-user-profile /> } @placeholder { <div class="profile-placeholder"> <div class="avatar-skeleton"></div> <div class="name-skeleton"></div> </div> } `})2. hydrate on viewport - When Scrolled Into View
Section titled โ2. hydrate on viewport - When Scrolled Into Viewโ@Component({ template: ` <!-- Perfect for below-the-fold content --> @defer (hydrate on viewport) { <heavy-product-reviews /> } @placeholder { <div class="reviews-skeleton"> Loading reviews... </div> } `})3. hydrate on idle - When Browser is Free
Section titled โ3. hydrate on idle - When Browser is Freeโ@Component({ template: ` <!-- Hydrates when browser has spare time --> @defer (hydrate on idle) { <analytics-dashboard /> } @placeholder { <div class="dashboard-skeleton"> Dashboard loading... </div> } `})4. hydrate on timer - After Specific Time
Section titled โ4. hydrate on timer - After Specific Timeโ@Component({ template: ` <!-- Hydrates after 3 seconds --> @defer (hydrate on timer(3s)) { <social-media-feed /> } @placeholder { <div class="feed-skeleton"> Loading social feed... </div> } `})5. Advanced: Preload Strategy
Section titled โ5. Advanced: Preload Strategyโ@Component({ template: ` <!-- Hydrates on interaction, but preloads when idle --> @defer (hydrate on interaction; preload on idle) { <expensive-component /> } @placeholder { <button class="load-component"> Click to activate component </button> } `})6. Never Hydrate - Static Content Only
Section titled โ6. Never Hydrate - Static Content Onlyโ@Component({ template: ` <!-- Never hydrates - pure static content --> @defer (hydrate never) { <static-content-component /> } @placeholder { <div class="static-placeholder"> Static content loading... </div> } `})7. Combining Multiple Triggers
Section titled โ7. Combining Multiple Triggersโ@Component({ template: ` <!-- Multiple hydration strategies --> @defer (hydrate on interaction; preload on timer(5s)) { <complex-feature /> } @placeholder { <div class="feature-placeholder"> Click to activate or wait 5 seconds </div> } `})๐๏ธ Real-World Implementation Patterns
Section titled โ๐๏ธ Real-World Implementation PatternsโE-commerce Product Page
Section titled โE-commerce Product Pageโ@Component({ template: ` <div class="product-page"> <!-- Critical: Load immediately --> <product-header [product]="product()" /> <product-images [images]="product().images" /> <add-to-cart-button [product]="product()" />
<!-- Hydrate when scrolled into view --> @defer (hydrate on viewport) { <product-reviews /> } @placeholder { <div class="reviews-skeleton">Loading reviews...</div> }
<!-- Hydrate on user interaction --> @defer (hydrate on interaction) { <product-comparison-tool /> } @placeholder { <button class="compare-btn">Compare Products</button> }
<!-- Hydrate when browser is idle, preload after 5s --> @defer (hydrate on idle; preload on timer(5s)) { <related-products /> } @placeholder { <div class="related-skeleton">Loading related products...</div> }
<!-- Never hydrate - pure static content --> @defer (hydrate never) { <newsletter-signup /> } @placeholder { <div class="newsletter-placeholder">Newsletter signup</div> } </div> `})export class ProductPageComponent { product = signal<Product>(/* product data */);}Dashboard Application
Section titled โDashboard Applicationโ@Component({ template: ` <div class="dashboard"> <!-- Critical: Always visible --> <dashboard-header /> <main-metrics />
<!-- Hydrate when scrolled into view --> @defer (hydrate on viewport) { <revenue-chart /> } @placeholder { <div class="chart-placeholder">๐ Revenue Chart</div> }
<!-- Hydrate on interaction, preload when idle --> @defer (hydrate on interaction; preload on idle) { <user-analytics /> } @placeholder { <div class="analytics-placeholder"> Click to load user analytics </div> }
<!-- Hydrate when browser is idle --> @defer (hydrate on idle) { <data-export-tool /> <advanced-reporting /> } @placeholder { <div class="tools-placeholder">Loading tools...</div> } </div> `})export class DashboardComponent {}๐ Performance Impact
Section titled โ๐ Performance ImpactโCore Web Vitals Improvements:
- LCP: 3.2s โ 1.1s (65% faster)
- INP: 340ms โ 89ms (74% better)
- CLS: 0.15 โ 0.02 (87% more stable)
- Bundle: 2.5MB โ 800KB initial (68% smaller)
๐ง Best Practices
Section titled โ๐ง Best Practicesโ1. Prioritize Critical Content
Section titled โ1. Prioritize Critical Contentโ// โ
Good - Critical content loads first<header /> <!-- Always hydrate --><main-content /> <!-- Always hydrate -->
@defer (on viewport) { <footer /> <!-- Defer non-critical -->}2. Use Meaningful Placeholders
Section titled โ2. Use Meaningful Placeholdersโ// โ
Good - Informative placeholder@defer (on interaction) { <advanced-search />} @placeholder { <button class="search-placeholder"> ๐ Click for advanced search </button>}
// โ Avoid - Generic placeholder@defer (on interaction) { <advanced-search />} @placeholder { <div>Loading...</div>}3. Keep It Simple
Section titled โ3. Keep It Simpleโ// โ
Good - Clear and focused@defer (hydrate on interaction) { <user-settings />} @placeholder { <button>โ๏ธ Settings</button>}
// โ Avoid - Overly complex@defer (hydrate on interaction; preload on timer(5s); on viewport) { <complex-component />}๐ฏ Quick Checklist
Section titled โ๐ฏ Quick Checklistโ- Enable SSR in your Angular application
- Add
withIncrementalHydration()to app config - Identify non-critical components for deferral
- Choose appropriate hydration triggers
- Add meaningful placeholders and loading states
- Test on different devices and network speeds
- Monitor Core Web Vitals improvements
- Use browser DevTools to verify hydration timing
๐ Next Steps
Section titled โ๐ Next Stepsโ- Zoneless Angular - Combine with zoneless for maximum performance
- New Control Flow - Use @defer with @if, @for, @switch
- Signal Forms - Reactive forms with incremental loading
Remember: Incremental hydration is like having a smart assistant that knows exactly when to bring you what you need - not too early, not too late, but precisely at the right moment! ๐ง