Why preload lazy routes

Route-level code splitting in Angular defers loading of JavaScript until a user navigates to a route, which keeps initial bundle size down but can put a delay in front of every subsequent navigation. The Angular router offers a built-in answer: a configurable preloadingStrategy that lets the router fetch and cache JavaScript for routes the user hasn't visited yet, while that user is still viewing the current route.

Two preloading strategies to consider

Angular ships with PreloadAllModules, which—as the name suggests—downloads every lazy-loaded route ahead of time. You can enable it by setting the property in the router configuration:

import { RouterModule, PreloadAllModules } from '@angular/router';
// …

RouterModule.forRoot([
  …
], {
  preloadingStrategy: PreloadAllModules
})
// …

Load the app and watch the Network panel in Chrome DevTools (open with Control+Shift+J, or Command+Option+J on Mac, then click the Network tab). With this strategy, modules like nyan-nyan-module.js and about-about-module.js get fetched in the background as soon as the app starts:

The PreloadAllModules strategy in action.

Because the router has registered route declarations for these preloaded modules, navigating to their URLs is instant.

When to avoid preloading everything

PreloadAllModules works well for apps with only a few lazy modules. With many modules, however, aggressive preloading consumes unnecessary network bandwidth through large downloads that might never be used. And because the router registers routes for every preloaded module, this strategy can trigger intensive main-thread computations that make the UI sluggish.

The quicklink library provides a more targeted approach. It leverages the IntersectionObserver API to preload only the modules that are associated with links visible in the current viewport. This keeps preloading traffic tied to what the user can actually click next.

To add this to a plain Angular application, install the ngx-quicklink package:

npm install --save ngx-quicklink

Then wire up QuicklinkStrategy as the preloadingStrategy and include the QuicklinkModule in the module where you configure your router:

import {QuicklinkStrategy, QuicklinkModule} from 'ngx-quicklink';
…

@NgModule({
  …
  imports: [
    …
    QuicklinkModule,
    RouterModule.forRoot([…], {
      preloadingStrategy: QuicklinkStrategy
    })
  ],
  …
})
export class AppModule {}

With this in place, opening the app triggers download of only nyan-nyan-module.js, because that's the only route linked in the visible content. Expanding the side navigation surface exposes the About link, which causes the router to fetch the about module at that point:

A demo of the quicklink preloading strategy.

The basic wiring above works for simple applications. In an application that has multiple lazy-loaded modules, links within those lazy modules will not be preload candidates unless QuicklinkModule is scoped into each one. A shared module solves this:

import { QuicklinkModule } from 'ngx-quicklink';
…

@NgModule({
  …
  imports: [
    QuicklinkModule
  ],
  exports: [
    QuicklinkModule
  ],
  …
})
export class SharedModule {}

Import that shared module (SharedModule) into every lazy-loaded module:

import { SharedModule } from '@app/shared/shared.module';
…

@NgModule({
  …
  imports: [
      SharedModule
  ],
  …
});

After that, quicklink can detect and preload visible router links regardless of how many lazy-loaded modules exist.

A more predictive preloading option

Quicklink already reduces network waste by preloading only what is in the user's immediate view. For further network efficiency, predictive preloading goes one step further: the Guess.js library analyzes Google Analytics reports (or data from other analytics providers) to anticipate which route the user will likely navigate to next and preloads only those JavaScript chunks. See the Guess.js Angular guide for setup instructions.

Decision points for preloading

When you launch your app with route-level code splitting, preloading solves the tight coupling between latency and initial navigation speed. Choose a strategy based on app scale:

  • Small app, few routes: start with Angular's built-in PreloadAllModules. It works out of the box and keeps configuration minimal.
  • Large app with many lazy routes: avoid excessive downloads and UI-thread work by picking a selective strategy, such as quicklink through ngx-quicklink, or a predictive strategy like Guess.js.

Configure whichever you choose via the preloadingStrategy property (and don't confuse it with preloadStrategy) on the Angular router.