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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A PreloadingStrategy lets you decide per-route whether a lazy module downloads in the background after the app boots.
- 2Route data is a flexible channel for passing configuration into infrastructure like preloaders.
- 3Returning of(null) from preload cleanly opts a route out without loading its bundle.
Related explainers
python
from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel, ConfigDict, EmailStr from sqlalchemy.orm import Session
Building a users router in FastAPI
routing
serialization
dependency-injection
Intermediate
7 steps
typescript
type Event = Record<string, unknown>; interface BatcherOptions { endpoint: string;
Batching analytics events in TypeScript
batching
buffering
async
Intermediate
8 steps
typescript
import { Processor, WorkerHost, OnWorkerEvent } from '@nestjs/bullmq'; import { Logger } from '@nestjs/common'; import { Job } from 'bullmq'; import { MailerService } from '../mailer/mailer.service';
Processing background email jobs in NestJS
job-queue
background-processing
dependency-injection
Intermediate
7 steps
typescript
import { Component, computed, inject, signal } from '@angular/core'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; import { CommonModule } from '@angular/common';
Building a signup stepper in Angular
signals
reactive-forms
form-validation
Intermediate
10 steps
typescript
type Change = | { kind: "added"; path: string; value: unknown } | { kind: "removed"; path: string; value: unknown } | { kind: "updated"; path: string; from: unknown; to: unknown };
Building a recursive deep-diff in TypeScript
recursion
discriminated-unions
type-guards
Intermediate
9 steps
typescript
import { DynamicModule, Module, Provider } from '@nestjs/common'; import Redis, { RedisOptions } from 'ioredis'; export const REDIS_CLIENT = Symbol('REDIS_CLIENT');
Building a dynamic Redis module in NestJS
dependency-injection
dynamic-modules
provider-tokens
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/a-custom-preloading-strategy-in-angular-explained-typescript-505d/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.