Angular module loading strategies, optimizing user experience

How to implement a module preloading strategy

When developing Angular applications, one of the key considerations is how to efficiently load and manage modules. How we handle module loading can have a significant impact on user experience and overall performance. In this post, we'll explore various module loading strategies in Angular and provide practical examples for each of them.

Lazy loading

One of Angular's most powerful features is the ability to load modules on-demand, a concept known as lazy loading. This means modules are loaded only when the user needs them, which can significantly improve initial load times. Below is an example of how this is implemented in the route file AppRoutingModule:

const routes: Routes = [
  {
    path: "contact",
    loadChildren: () =>
      import("./contact/contact.module").then((m) => m.ContactModule),
  },
  {
    path: "about",
    loadChildren: () =>
      import("./about/about.module").then((m) => m.AboutModule),
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

StandAlone API version

export const routes: Routes = [
  {
    path: "contact",
    loadChildren: () =>
      import("./contact/contact.routes").then((m) => m.CONTACT_ROUTES),
  },
  {
    path: "about",
    loadChildren: () =>
      import("./about/about.routes").then((m) => m.ABOUT_ROUTES),
  },
];

Directly lazy loading a standalone component

export const routes: Routes = [
  {
    path: "contact",
    loadComponent: () =>
      import("./contact/contact.component").then((m) => m.ContactComponent),
  },
  {
    path: "about",
    loadComponent: () =>
      import("./about/about.component").then((m) => m.AboutComponent),
  },
];
bootstrapApplication(AppComponent, {
  providers: [provideRouter(routes)],
});

Preload all modules

If you want to preload all modules in your application upfront, you can use the PreloadAllModules strategy. This speeds up user navigation after loading all modules at once. Here's how to enable this strategy:

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      preloadingStrategy: PreloadAllModules,
    }),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

StandAlone API version

bootstrapApplication(AppComponent, {
  providers: [provideRouter(routes, withPreloading(PreloadAllModules))],
});

Custom module preloading

What if you only want to preload specific modules upfront? You can create a custom preloading strategy, loading only the modules that are most crucial for the user experience. Here's an example of a custom preloading strategy:

@Injectable({
  providedIn:'root'
})
export class PreloadingStrategyService implements PreloadingStrategy {
  private preloadedModules: string[] = [];

  preload(route: Route, load: () => Observable): Observable {
    if (route.data && route.data['preload'] && route.path) {
      this.preloadedModules.push(route.path);
      return load();
    } else {
      return of(null);
    }
  }
}
const routes: Routes = [
  {
    path: "contact",
    loadChildren: () =>
      import("./contact/contact.module").then((m) => m.ContactModule),
  },
  {
    path: "about",
    loadChildren: () =>
      import("./about/about.module").then((m) => m.AboutModule),
    data: { preload: true },
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      preloadingStrategy: PreloadingStrategyService,
    }),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

StandAlone API version

bootstrapApplication(AppComponent, {
  providers: [provideRouter(routes, withPreloading(PreloadingStrategyService))],
});

Another interesting option is the ngx-quicklink library, which loads modules as the links appear on the screen. This enhances user-perceived speed by loading modules in the background. To use it, you first need to install the library:

npm i ngx-quicklink

Then, you can enable it as follows:

import { QuicklinkStrategy } from "ngx-quicklink";

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      preloadingStrategy: QuicklinkStrategy,
    }),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

StandAlone API version

import { quicklinkProviders, QuicklinkStrategy } from 'ngx-quicklink';

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes, withPreloading(QuicklinkStrategy)),
    quicklinkProviders
  ]
})

Import the QuicklinkDirective in all your standalone components that use preloading:

import { RouterModule } from '@angular/router';
import { QuicklinkDirective } from 'ngx-quicklink';

@Component({
  standalone: true,
  imports: [RouterModule, QuicklinkDirective],
    template: `
    <a routerLink="/form">Form</a>
  `,
})

Conclusion:

In summary, Angular offers several module loading strategies to optimize user experience and application performance. The choice of the right strategy depends on the specific needs of your project. Lazy loading, preloading, custom loading, and ngx-quicklink are powerful tools you can use to ensure your users enjoy a smooth and fast experience.

Explore these strategies and choose the one that best suits your requirements!

Did you find this article valuable?

Support Rubén Peregrina by becoming a sponsor. Any amount is appreciated!