Deferrable Views, introduced in Angular 17, is a powerful feature that allow you to defer loading of content until it’s needed. This improves intial load performance by lazy loading components at the template level.
Basic Syntax and Usage
@defer {
<expensive-component />
}
Defer with Placeholder
@defer {
<expensive-component />
} @placeholder {
<p>Content is loading...</p>
}
Trigger Conditions
Viewport Trigger
Loads content when it enters the viewport:
@defer (on viewport) {
<product-carousel />
} @placeholder {
<div class="carousel-placeholder">Carousel loading...</div>
}
Interaction Trigger
Loads content after user interaction:
<button #termsButton>View Terms</button>
@defer (on interaction(termsButton)) {
<terms-and-conditions />
} @placeholder {
<p>Click to load terms and conditions</p>
}
Idle Trigger
Loads content during browser idle time:
@defer (on idle) {
<non-critical-content />
} @placeholder {
<p>Loading additional content...</p>
}
Timer Trigger
Loads content after a specified time:
@defer (on timer(5000)) { // 5 seconds
<delayed-content />
} @placeholder {
<p>Content will load shortly...</p>
}
Hover Trigger
Loads content when user hovers over an element:
<div #hoverArea>Hover here</div>
@defer (on hover(hoverArea)) {
<hover-content />
} @placeholder {
<p>Hover to load content</p>
}
Complete Example with All States
<!-- dashboard.component.html -->
<div class="dashboard">
<h1>Dashboard</h1>
<!-- Critical content loaded immediately -->
<user-summary></user-summary>
<!-- Deferred analytics section -->
@defer (on viewport) {
<analytics-dashboard />
} @placeholder {
<div class="placeholder-box">
<p>Analytics loading...</p>
</div>
} @loading {
<loading-spinner />
} @error {
<error-message message="Failed to load analytics" />
}
<!-- Deferred with multiple triggers -->
<button #loadBtn>Load Reports</button>
@defer (on interaction(loadBtn) || timer(10000)) {
<detailed-reports />
} @placeholder {
<p>Click to load reports or wait for automatic load</p>
}
</div>
Prefetching Strategy
You can control when the deferred content starts loading:
@defer (on viewport; prefetch on hover) {
<product-details />
}
Available Prefetch Options
// Prefetch on hover
@defer (prefetch on hover) { ... }
// Prefetch when parent is visible
@defer (prefetch on viewport) { ... }
// Prefetch after idle
@defer (prefetch on idle) { ... }
// Prefetch after specific time
@defer (prefetch on timer(2000)) { ... }
Real-World Example: E-commerce Product Page
// product-page.component.ts
@Component({
selector: 'app-product-page',
templateUrl: './product-page.component.html'
})
export class ProductPageComponent {
product = {
id: 1,
name: 'Premium Headphones',
price: 199.99,
description: 'High-quality wireless headphones'
};
}
<!-- product-page.component.html -->
<div class="product-page">
<!-- Critical content loaded immediately -->
<header class="product-header">
<h1>{{ product.name }}</h1>
<p class="price">${{ product.price }}</p>
</header>
<!-- Product images loaded when in viewport -->
@defer (on viewport) {
<product-gallery [productId]="product.id" />
} @placeholder {
<div class="image-placeholder">
<p>Loading product images...</p>
</div>
} @loading {
<loading-spinner />
}
<!-- Reviews loaded on interaction -->
<button #reviewsBtn>Show Reviews</button>
@defer (on interaction(reviewsBtn); prefetch on hover) {
<product-reviews [productId]="product.id" />
} @placeholder {
<p>Click to load customer reviews</p>
}
<!-- Related products loaded when user scrolls near -->
@defer (on viewport) {
<related-products [currentProductId]="product.id" />
} @placeholder {
<div class="related-placeholder">
<p>Discovering related products...</p>
</div>
}
<!-- Technical specs loaded on tab click -->
<div #specsTab>Technical Specifications</div>
@defer (on interaction(specsTab)) {
<technical-specs [productId]="product.id" />
} @placeholder {
<p>Click to view technical specifications</p>
}
</div>