Migrating from Custom Lazy Loading components to defer block
Published Mar 11, 2024

Migrating from Custom Lazy Loading components to defer block

Lazy loading components in Angular has not been an easy task for a long time. We had to go over ngComponentOutlet and dynamic imports and also handle different kind of events to load the desired component.

Example in the Movies app

In the movies app we have an example in the app-shell.component.ts where we lazy load an account menu component when the user clicks on the account button.

<div class="account-menu-dropdown">
  <button 
    type="button" 
    class="profile-button" 
    (mouseenter)="ui.loadAccountMenu()">
      <div class="focus-overlay"></div>
      <fast-svg name="account" />
  </button>
  <div class="profile-menu-content">
    <ng-container 
        *rxLet="accountMenuComponent$; suspense: loading" 
        [lazy]="accountMenuComponent$" />
    <ng-template #loading> Loading...</ng-template>
  </div>
</div>
type Actions = {
  ...
  loadAccountMenu: void;
};

export class AppShellComponent {
  readonly ui = rxActions<{ loadAccountMenu: void }>();

  accountMenuComponent$ = this.ui.loadAccountMenu$.pipe(
    switchMap(() =>
      import('./account-menu/account-menu.component').then((x) => x.default)
    ),
    shareReplay(1)
  );
}

So, we create an ui actions object and we have a loadAccountMenu action that we call when the user clicks on the account button. We have a accountMenuComponent$ observable that we use to lazy load the AccountMenuComponent when the user clicks on the account button.

The observable is then used in the *rxLet directive to render the AccountMenuComponent when it's loaded.

Migrating to defer block

Defer block is a new feature in Angular that allows us to defer the rendering of a component until it's loaded.

We can use the defer block to achieve the same result as the previous example.

<div class="account-menu-dropdown">
  <button #accountButton type="button" class="profile-button">
    <div class="focus-overlay"></div>
    <fast-svg name="account" />
  </button>
  <div class="profile-menu-content">
    @defer (on hover(accountButton)) {
        <app-account-menu />
    } @placeholder {
        Loading...
    }
  </div>
</div>

That's it. We don't need to use any observables or actions, or register any listeners to load the AccountMenuComponent. We can safely remove the observable, the *rxLet directive and the actions object.

The on hover() will register an event listener on the accountButton and when the user hovers over the button, the AccountMenuComponent will be loaded and rendered.

More info in the defer block docs;

Thanks for reading!

If this article was interesting and useful to you, and you want to learn more about Angular, support me by buying me a coffee ☕️ or follow me on X (formerly Twitter) @Enea_Jahollari where I tweet and blog a lot about Angular latest news, signals, videos, podcasts, updates, RFCs, pull requests and so much more. 💎


Share this article:

Previous articles

Don't miss out on our previous articles.