Skip to content

Loading Content in Complex Angular Applications with the defer block

Published:  at 07:56 PM

One of the new features introduced in the latest versions of Angular is the concept of deferrable views, enabling the delay of loading certain parts of a page using the @defer syntax.

This is particularly useful in scenarios where we have parts of the application that are not critical to the initial load, such as analytics, social media widgets, or other non-essential content. By deferring these parts, we can reduce the initial load time and improve the overall performance.

How to use the defer block

Using the defer block is pretty straightforward.

@Component({
  selector: 'hello-world-component',
  standalone: true,
  template: `
    <h1>Hello World</h1>
    @defer {
      <p>This is a deferred paragraph</p>
    }
  `,
})

The content inside the defer block will be loaded from a separate chunk only when the browser is idle, which means that it will not impact the initial rendering of the page.

Defining placeholder, triggers, loading and error states

Even though the default behaviour can be good enough for many cases, we can define custom loading and error states.

@placeholder

By default, nothing is displayed while the content is loading. We can change this by providing a custom template using the @placeholder block.

@defer {
  <p>This is a deferred paragraph</p>
} @placeholder {
  <p>Loading...</p>
}

To prevent fast flickering, the placeholder block accepts an optional parameter to specify the minimum amount of time that the placeholder should be displayed.

@defer {
  <p>This is a deferred paragraph</p>
} @placeholder(minimum 500ms) {
  <p>Loading...</p>
}

Triggers

As we said, the content of the defer block is loaded when the browser is idle by default. We can change this behaviour by providing a custom trigger.

// Load the content when it is visible in the viewport
@defer(on viewport) {
  <p>This is a deferred paragraph</p>
}

// Load the content based on a custom condition
@defer(when condition) {
  <p>This is a deferred paragraph</p>
}

// We can also combine multiple triggers that will be evaluated as OR conditions
@defer(on viewport; when condition) {
  <p>This is a deferred paragraph</p>
}

Angular provides a set of built-in triggers that we can use:

It’s important to note that once the content is loaded, the trigger condition is not evaluated anymore, and the content will not be unloaded if the trigger condition is not met anymore.

@loading

If we use a custom trigger it can be recommended to define a loading state to distinguish the placeholder (e.g. a static skeleton screen) from the loading state that occurs when the trigger condition is met.

@defer(on viewport) {
  <p>This is a deferred paragraph</p>
} @placeholder {
  <p>Here will be a paragraph</p>
} @loading {
  <p>Loading...</p>
}

@error

The error block can be used to define when an error occurs while loading the deferred content, such as a network failure while loading the chunk.

@defer(on viewport) {
  <p>This is a deferred paragraph</p>
} @placeholder {
  <p>Here will be a paragraph</p>
} @loading {
  <p>Loading...</p>
} @error {
  <p>Error loading the deferred content</p>
}

The defer block is a powerful feature that can help us improve the performance of our Angular applications by reducing the initial load time and improving the user experience, especially on complex applications when running on slow networks.

As always when it comes to performance, it’s important to remember that premature optimisation is the root of all evil, and we should only use the defer block when we actually notice or suspect a possible performance issue.