Back to all posts

Deferrable Views In Angular


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>