How to Implement Deferrable Views in Angular 17 for Faster Load Times
Tags: angular, javascript, webdev, typescript
Published: 2024-05-12 14:43:45

Among the array of optimization strategies, deferrable views, also known as **@****defer** blocks, emerge as a powerful tool to streamline initial load times and enhance user experience. Deferrable views offer developers the ability to defer the rendering of certain components until they are needed. By utilizing **@****defer** blocks strategically, developers can delay the loading of non-critical components, reducing the initial bundle size and enhancing the perceived speed of the application. In this article, we delve into the benefits, syntax, and implementation of deferrable views in Angular applications.

## Why use Deferrable Views?
Deferrable views, also referred to as **@**defer blocks, offer several compelling benefits:

1. **Reduced Initial Bundle Size:** By deferring the loading of certain dependencies, developers can minimize the initial bundle size of their Angular applications. This reduction in initial payload size contributes to faster load times and improved performance.
2. Deferring heavy components until later can significantly improve metrics such as **Largest Contentful Paint** (LCP measures the time it takes for the largest content element within the viewport to become visible to the user) and **Time to First Byte** (TTFB measures the time taken for the browser to receive the first byte of data from the server after initiating a request).
3. **Improved User Experience:** Deferrable views contribute to a smoother user experience by prioritizing the rendering of critical content while non-essential components load in the background.

## Blocks in Deferrable Views
**@****defer**

The primary **@****defer** block encapsulates the content that is lazily loaded.

This content remains unrendered initially and becomes visible once specified triggers or conditions are met, and dependencies are fetched.

By default, a **@****defer** block is triggered when the browser state becomes idle.

**@****placeholder**

The optional **@****placeholder** block allows developers to specify content to display before the **@****defer** block is triggered.

This placeholder content serves as a temporary visual indicator and is replaced with the main content upon completion of loading.

The **@****placeholder** block accepts an optional parameter (**minimum**) to specify the **minimum** amount of time that this placeholder should be shown.

This parameter (minimum) exists to prevent fast flickering of placeholder content in the case that the deferred dependencies are fetched quickly.

```
@defer {
   <large-component />
} @placeholder (minimum 500ms) {
   <p>Placeholder content</p>
}
```
**@loading**

The **@loading** block allows you to declare content that will be shown during the loading of any deferred dependencies.

This block is optional and commonly used to show loading indicators such as spinners.

The **@loading** block accepts two optional parameters (**minimum & after**) to specify the **minimum** amount of time that this placeholder should be shown and amount of time to wait **after** loading begins before showing the loading template.

The **@loading** state is bypassed when resources have been **prefetched**.

```
@defer {
    <large-component />
} @loading (after 100ms; minimum 1s) {
    <img alt="loading..." src="loading.gif" />
}
```
**@****error**
In case of deferred loading failure, the **@****error** block allows developers to declare content to be shown. This block, like **@****placeholder** and **@****loading**, is optional.

```
@defer {
  <calendar-cmp />
} @error {
  <p>Failed to load the calendar</p>
}
```

## Triggers

Triggers determine when a **@****defer** block is activated, replacing placeholder content with lazily loaded content.

NOTE:

· Nested **@****defer** blocks should have different conditions to prevent cascading requests.

· During server-side rendering (SSR) or static site generation (SSG), **@**defer blocks render their placeholders by default, disregarding triggers

Below are various triggers that cater to different user interactions and states.

**- on:** Specifies trigger conditions using predefined triggers such as idle, viewport, interaction, hover, immediate, and timer.
**- when:** Specifies conditions using boolean expressions.

**on idle**

The **idle** trigger initiates deferred loading when the browser reaches an idle state. This is the default behavior for a **@****defer** block.

**on viewport**

The **viewport** trigger activates deferred loading when the specified content enters the viewport. By default, the placeholder acts as the element watched for entering the viewport.

Alternatively, developers can designate a template reference variable as the element to watch for viewport entry.

```
<div #notification>Message body..</div>
@defer (on viewport(notification)) {
  <notification-popup />
}
```
**on interaction**

The **interaction** trigger initiates deferred loading when the user interacts with the specified element through click or keydown events. By default, the placeholder serves as the interaction element.

Alternatively, developers can specify a template reference variable as the interaction trigger.

```
<button type="button" #greeting>Hello!</button>
@defer (on interaction(greeting)) {
  <greetings-cmp />
}
```
**on hover**

The **hover** trigger triggers deferred loading when the mouse hovers over the specified element.

Alternatively, developers can designate a template reference variable as the hover trigger.

**on immediate**

The **immediate** trigger initiates deferred loading immediately after client rendering completes, bypassing any further delays

**on timer**

The timer **trigger** activates deferred loading after a specified duration, specified in milliseconds or seconds.

```
@defer (on timer(500ms)) {
  <greetings-cmp />
}
```
**Prefetching**

Prefetching enables the proactive fetching of dependencies based on specified conditions, enhancing resource availability before actual usage

The syntax for specifying prefetch conditions is similar to that of the main **@****defer** conditions. This includes the use of when and on to declare the triggers for prefetching

```
**@**defer (on interaction; prefetch on idle) {
  <product-details-cmp />
}
```

## Example

Now let’s illustrate how to use deferrable views by creating a simple News App to explore how by deferring the rendering of certain components, we can enhance performance, streamline initial load times, and create a better user experience

Run the following command to generate a new project:

```
ng new news-app
```
Run the following command to generate a new interface:

```
ng generate interface article
```
Now, let’s modify the article.ts file in the **src/app** directory to implement the Article interface:

```
export interface Article {
    title: string;
    imageUrl: string;
    comments: string[];
  }
```
Run the following command to generate a new service:

```
ng generate service news
```
Now, let’s modify the **news.service.ts** file in the **src/app** directory to implement the **NewsService**:

```
import { Injectable } from '@angular/core';
import { Article } from './article'; 

@Injectable({
  providedIn: 'root'
})
export class NewsService {
  private articles: Article[] = [
    { 
      title: 'Article 1', 
      imageUrl: 'https://source.unsplash.com/300x300', 
      comments: ['Article 1 Comment 1', 'Article 1 Comment 2', 'Article 1 Comment 3'] 
    },
    { 
      title: 'Article 2', 
      imageUrl: 'https://source.unsplash.com/300x300', 
      comments: ['Article 2 Comment 1', 'Article 2 Comment 2', 'Article 2 Comment 3'] 
    },
    { 
      title: 'Article 3', 
      imageUrl: 'https://source.unsplash.com/300x300', 
      comments: ['Article 3 Comment 1', 'Article 3 Comment 2', 'Article 3 Comment 3'] 
    },
    // Add more articles as needed
  ];

  constructor() {}

  getArticles(): Article[] {
    // Simulate fetching articles from an API
    return this.articles;
  }
}
```
Run the following command to generate a new component:

```
ng generate component article
```
Now, let’s modify the **article.component.ts**:

```
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Article } from '../article';

@Component({
  selector: 'app-article',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './article.component.html',
  styleUrl: './article.component.css'
})
export class ArticleComponent {
  @Input()
  article!: Article;

  constructor() {}
}
```
Now, let’s modify the **article.component.html** to implement the deferrable views:

```
<div class="article">
    <h3>{{ article.title }}</h3>
    <!-- Image wrapped in a @defer block -->
    @defer (on viewport) {
      <img [src]="article.imageUrl" alt="Article Image">
    } @placeholder(minimum 500ms) {
      <!-- Placeholder content while loading -->
      <div class="placeholder">
        <p>Image placeholder...</p>
      </div>
    } @loading {
      <!-- Loading indicator -->
      <div class="loading">
        <img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZTJpbzV1eWpvOHdpZTRjMXF4NGhqMzlleWM1Y2Zwc2U1a292dm9jbSZlcD12MV9naWZzX3NlYXJjaCZjdD1n/uIJBFZoOaifHf52MER/giphy.gif" alt="Loading...">
      </div>
    } @error {
      <!-- Error message -->
      <div class="error">
        <p>Failed to load image. Please try again later.</p>
      </div>
    }
    
    <!-- Comments section wrapped in a @defer block -->
    @defer (on interaction) {
      <div class="comments">
        <h4>Comments</h4>
        <ul>
          <li *ngFor="let comment of article.comments">{{ comment }}</li>
        </ul>
      </div>
    } @placeholder {
      <!-- Placeholder content while loading -->
      <div class="placeholder">
        <p>Comments placeholder...</p>
      </div>
    } @loading {
      <!-- Loading indicator -->
      <div class="loading">
        <img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZTJpbzV1eWpvOHdpZTRjMXF4NGhqMzlleWM1Y2Zwc2U1a292dm9jbSZlcD12MV9naWZzX3NlYXJjaCZjdD1n/uIJBFZoOaifHf52MER/giphy.gif" alt="Loading...">
      </div>
    } @error {
      <!-- Error message -->
      <div class="error">
        <p>Failed to load comments. Please try again later.</p>
      </div>
    }
  </div>
```
Run the following command to generate a new component:

```
ng generate component news
```
Now, let’s modify the **news.component.ts** file to use the NewsService:

```
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NewsService } from '../news.service';
import { ArticleComponent } from '../article/article.component';
import { Article } from '../article';

@Component({
  selector: 'app-news',
  standalone: true,
  imports: [CommonModule, ArticleComponent],
  templateUrl: './news.component.html',
  styleUrl: './news.component.css'
})
export class NewsComponent implements OnInit {
  articles: Article[] = [];

  constructor(private newsService: NewsService) {}

  ngOnInit(): void {
    this.articles = this.newsService.getArticles();
  }
}
```
Now, let’s modify the **news.component.html** to display the news articles:

```
<div *ngFor="let article of articles">
    <app-article [article]="article"></app-article>
  </div>
```
Now, let’s modify the **app.component.ts** file to use the NewsComponent:

```
import { Component } from '@angular/core';
import { NewsComponent } from './news/news.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [NewsComponent],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {
  title = 'news-app';
}
```
Finally, let’s update the **app.component.html** file to remove the default content and render the news component:

```
<app-news></app-news>
```
Now, you can run the application using the following command:

```
ng serve
```

## Conclusion

In conclusion, Deferrable views in Angular applications offer developers a powerful mechanism to optimize performance and improve user experience. By strategically deferring the loading of non-critical components, developers can achieve faster load times, reduce bundle sizes, and enhance overall user experience.

To get the whole code, check the link below👇👇👇
[https://github.com/anthony-kigotho/news-app](https://github.com/anthony-kigotho/news-app)

## CTA
Many developers and learners encounter tutorials that are either too complex or lacking in detail, making it challenging to absorb new information effectively.

[Subscribe to our newsletter](https://bytewave.substack.com/) today, to access our comprehensive guides and tutorials designed to simplify complex topics and guide you through practical applications.