100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
TypeScript

Lazy Loading Feature Routes

Discover how to split an Angular application into smaller JavaScript bundles that load on demand as users navigate, dramatically improving initial load performance.

Routing with the Angular RouterIntermediate9 min readJul 9, 2026
Analogies

Lazy Loading Feature Routes

As an Angular application grows, bundling every feature into a single JavaScript file means users pay the download and parse cost of code they may never visit — an admin panel, a settings page, a rarely-used reporting module. Lazy loading solves this by splitting the application into smaller chunks that the build tooling (esbuild, via the Angular CLI) emits as separate files, which the router then fetches over the network only when the user actually navigates to a route that needs them. This is one of the highest-leverage performance techniques available in Angular and requires no manual bundler configuration — it's driven entirely by how routes are declared.

🏏

Cricket analogy: Bundling every feature into one file is like a broadcaster shipping every camera angle from every ground worldwide in one feed even though a viewer only watches the Lord's Test; lazy loading only streams the one match the fan actually tunes into.

loadComponent for Standalone Components

For a single standalone component, lazy loading is expressed with loadComponent, a function returning a dynamic import() that resolves to the component class. The Angular CLI's build system automatically detects the dynamic import() call and generates a separate chunk for it. This replaces the pre-standalone pattern of lazy-loading an entire NgModule via loadChildren, which required wrapping even a single component in a dedicated feature module just to defer it.

🏏

Cricket analogy: loadComponent for a single standalone component is like fielding a specialist death-bowler brought on only for the final over, resolved via a dynamic call the moment he's needed, replacing the older approach of keeping an entire reserve squad (NgModule) on standby just to field one player.

typescript
// app.routes.ts
import { Routes } from '@angular/router';

export const routes: Routes = [
  { path: '', loadComponent: () => import('./home/home.component').then(m => m.HomeComponent) },
  {
    path: 'settings',
    loadComponent: () => import('./settings/settings.component').then(m => m.SettingsComponent),
    title: 'Settings',
  },
];

loadChildren for Grouped Feature Routes

When a whole feature area has multiple related routes (e.g. an admin section with a dashboard, users list, and settings page), it's cleaner to lazy-load a nested Routes array in one chunk using loadChildren. The child file exports its own Routes array, and the parent route's path acts as a prefix for every child path. This groups related code into one network request instead of many small ones, and keeps the parent routing file uncluttered.

🏏

Cricket analogy: Lazy-loading a nested Routes array with loadChildren for an admin section is like chartering one team bus that carries the captain, coach, and physio together for an away series, instead of booking three separate flights, with the tour's base city acting as the prefix for every fixture on that leg.

typescript
// app.routes.ts
export const routes: Routes = [
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
    canActivate: [authGuard], // guards on the parent route protect all children
  },
];

// admin/admin.routes.ts
import { Routes } from '@angular/router';

export const ADMIN_ROUTES: Routes = [
  { path: '', loadComponent: () => import('./admin-dashboard.component').then(m => m.AdminDashboardComponent) },
  { path: 'users', loadComponent: () => import('./admin-users.component').then(m => m.AdminUsersComponent) },
];

Angular's provideRouter() also accepts withPreloading(PreloadAllModules) (or a custom PreloadingStrategy), which fetches lazy chunks in the background after the initial page finishes loading, so subsequent navigations feel instant while the first paint still stays fast — a middle ground between eager loading everything and loading strictly on demand.

A common mistake is importing a lazily-loaded component's class directly elsewhere in eagerly-loaded code (e.g. adding it to another component's imports array). This creates a static reference that bundlers can trace, pulling the 'lazy' chunk back into the main bundle and silently defeating the code-splitting entirely.

  • Lazy loading splits the app into separate JS chunks fetched only when a route is visited, shrinking the initial bundle.
  • loadComponent lazily loads a single standalone component via a dynamic import().
  • loadChildren lazily loads a nested Routes array for a whole feature area, grouping related routes into one chunk.
  • Guards placed on the parent route (e.g. canActivate) apply to all of its lazily-loaded children.
  • withPreloading(PreloadAllModules) fetches lazy chunks in the background after initial load for faster subsequent navigation.
  • Avoid eagerly importing a lazy component's class elsewhere, which silently merges it back into the main bundle.

Practice what you learned

Was this page helpful?

Topics covered

#TypeScript#AngularStudyNotes#WebDevelopment#LazyLoadingFeatureRoutes#Lazy#Loading#Feature#Routes#StudyNotes#SkillVeris