typescript 48 lines · 7 steps

A custom preloading strategy in Angular

Control which lazy-loaded route modules preload — and when — using route data flags and a delay.

Explained by highlit
1import { Injectable } from '@angular/core';
2import { PreloadingStrategy, Route, Routes, RouterModule } from '@angular/router';
3import { NgModule } from '@angular/core';
4import { Observable, of, timer } from 'rxjs';
5import { mergeMap } from 'rxjs/operators';
6 
7@Injectable({ providedIn: 'root' })
8export class SelectivePreloadStrategy implements PreloadingStrategy {
9 preload(route: Route, load: () => Observable<unknown>): Observable<unknown> {
10 if (!route.data?.['preload']) {
11 return of(null);
12 }
13 
14 const delay = (route.data['preloadDelay'] as number) ?? 0;
15 return timer(delay).pipe(mergeMap(() => load()));
16 }
17}
18 
19const routes: Routes = [
20 {
21 path: 'dashboard',
22 loadChildren: () =>
23 import('./dashboard/dashboard.module').then((m) => m.DashboardModule),
24 data: { preload: true },
25 },
26 {
27 path: 'reports',
28 loadChildren: () =>
29 import('./reports/reports.module').then((m) => m.ReportsModule),
30 data: { preload: true, preloadDelay: 3000 },
31 },
32 {
33 path: 'admin',
34 loadChildren: () =>
35 import('./admin/admin.module').then((m) => m.AdminModule),
36 data: { preload: false },
37 },
38];
39 
40@NgModule({
41 imports: [
42 RouterModule.forRoot(routes, {
43 preloadingStrategy: SelectivePreloadStrategy,
44 }),
45 ],
46 exports: [RouterModule],
47})
48export class AppRoutingModule {}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A PreloadingStrategy lets you decide per-route whether a lazy module downloads in the background after the app boots.
  2. 2Route data is a flexible channel for passing configuration into infrastructure like preloaders.
  3. 3Returning of(null) from preload cleanly opts a route out without loading its bundle.

Related explainers

typescript
import { Component, computed, DestroyRef, inject, input, output, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { interval, map, takeWhile } from 'rxjs';
 

How a signal-driven countdown works in Angular

signals reactivity rxjs
Intermediate 8 steps
typescript
type TokenType = "keyword" | "string" | "comment" | "number" | "text";
 
interface Token {
  type: TokenType;

How a regex tokenizer highlights code

tokenizer regex lexing
Intermediate 10 steps
javascript
const express = require('express');
const router = express.Router();
 
router.get('/articles/:slug', async (req, res, next) => {

Conditional GET caching in Express

http-caching conditional-get routing
Intermediate 8 steps
typescript
import { Component, inject, signal } from '@angular/core';
import { CdkDragDrop, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
import { HttpClient } from '@angular/common/http';
import { finalize } from 'rxjs';

Drag-and-drop reordering with signals in Angular

drag-and-drop signals optimistic-update
Intermediate 8 steps
typescript
type Middleware<TIn, TOut> = (ctx: TIn) => Promise<TOut> | TOut;
 
class Pipeline<TIn, TOut> {
  private constructor(private readonly run: Middleware<TIn, TOut>) {}

A type-safe async middleware pipeline

generics type-safety middleware
Advanced 9 steps
typescript
type Countdown = {
  days: number;
  hours: number;
  minutes: number;

Building a self-stopping countdown timer

date-math closures timers
Intermediate 9 steps

Share this explainer

Here's the card — post it anywhere.

A custom preloading strategy in Angular — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code