Angular Lazy Loading Guide: Boost App Performance

Angular Lazy Loading Guide_ Boost App Performance

Does your Angular app take too long to load?

If users stare at a blank page for even a few seconds, they may leave. Performance is not just a technical concern—it directly affects user experience, SEO rankings, and conversion rates.

In this guide, you’ll learn:

  • What lazy loading is
  • Why it improves performance
  • Different types of lazy loading in Angular (with examples)
  • When to use preloading vs on-demand loading
  • How Angular 16+ simplifies this with @defer

This article is written for beginner to intermediate Angular developers who want to build faster and more scalable applications.

What is Lazy Loading?

Lazy loading is a technique where Angular loads modules, components, and services only when they are required, instead of loading everything at the start. This keeps the initial load smaller and faster.

In simple terms:
Load features only when they are needed — not all at once.

Imagine a restaurant.
You don’t receive the entire menu’s food at your table. You only get what you order.

Angular works the same way:

  • Without lazy loading → Everything loads at startup
  • With lazy loading → Only essential parts load first

This reduces the initial bundle size, which improves startup speed.

Why Lazy Loading?

In large applications, loading all features upfront can slow down the first screen your users see. Lazy loading fixes this by delivering just the necessary parts first and fetching the rest only when the user actually needs them.

When Angular loads everything at once:

  • JavaScript bundle size increases
  • Browser parsing time increases
  • Memory usage increases
  • Time to Interactive (TTI) becomes slower

Lazy loading helps by:

  • Delivering a smaller initial bundle
  • Reducing DOM complexity at startup
  • Downloading features only when required
  • Improving perceived performance

Types of Lazy Loading in Angular:

1. Route-Based Lazy Loading:

This is the standard and recommended approach

Without Lazy Loading (Eager Loading)

All feature modules are loaded at startup, even if the user never visits those routes. This increases the bundle size and forces the DOM to load and render everything upfront, which slows down the initial page load.

App.routing.module.ts :

const routes: Routes = [ 
  {path: 'home', component: HomeComponent }, 
  {path: 'admin', component: AdminComponent }, 
  {path: 'reports', component: ReportsComponent } 
];

Problem:

  • AdminComponent loads even if user never visits /admin
  • ReportsComponent loads even if unused
  • Large initial bundle

With lazy Loading

Only the required module is fetched when the user navigates to its route. This means the DOM doesn’t get bloated with unnecessary components at startup, keeping the initial page lightweight. As a result, performance improves with faster load times and smoother user experience.

App.routing.Module.ts :

const routes: Routes = [ 
  {path: 'home', component: HomeComponent }, 
  { 
    path: 'admin', 
    loadChildren: () => 
      import('./admin/admin.module').then(m => m.AdminModule) 
  }, 
  { 
    path: 'reports', 
    loadChildren: () => 
      import('./reports/reports.module').then(m => m.ReportsModule) 
  } 
];

What Happens Now?

  • AdminModule loads only when /admin is visited
  • ReportsModule loads only when /reports is visited
  • Angular creates separate JS chunks

2. Component-Level Lazy Loading (Angular 14+)

Angular 14 introduced standalone components and loadComponent. With Angular 14+, you can lazy load standalone components directly in routes using loadComponent.

Without Lazy Loading
In eager loading, components are imported and bundled at startup, even if they are rarely used.

App.routing.module.ts :

{ 
  path: 'profile', 
  component: ProfileComponent 
}

Here: Profile loads at startup.

With lazy Loading

ProfileComponent is loaded only when their route is accessed, keeping the DOM clean and the app faster at startup.

App.routing.module.ts

{ 
  path: 'profile', 
  loadComponent: () => 
    import('./profile/profile.component').then(c => c.ProfileComponent) 
}

Now:

  • ProfileComponent loads only when /profile is accessed
  • Keeps startup bundle smaller

3. Preloading Strategy:

Lazy loading may cause a slight delay the first time a user visits a route.
Angular provides preloading strategies to fix this.

Without Preloading (Default)

By default, Angular loads lazy modules only when the user navigates to their routes.

RouterModule.forRoot(routes, { preloadingStrategy: NoPreloading })

Possible issue:

  • First-time navigation to a lazy route can cause a delay (since the module has to be fetched at that moment).
  • For example, going to ‘/reports’ may feel slightly slower because Angular must download ReportsModule on the spot.

With Preloading

Preloading solves this problem by loading lazy modules in the background after the app starts — so when the user finally navigates to them, they’re already available.

RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })

Angular will preload all lazy modules (like ReportsModule, AdminModule, etc.) in the background immediately after the app loads.

Downside:

  • May preload modules users never use.
  • Not ideal for low-bandwidth environments.

When to Use Preloading?

Use it when:

  • You expect users to visit most routes
  • Your users have stable internet
  • You want smoother navigation

Avoid it when:

  • App is very large
  • Users are on mobile networks
  • Bandwidth usage must be optimized

4. On-Demand (Manual) Lazy Loading

Sometimes, you may want to load a component only after a specific user action. Example: Load Reports after clicking a button.

App.component.html:

<button (click)="openReports()">Load Reports</button>  
<ng-container *ngComponentOutlet="dynamicComponent"></ng-container>

App.component.ts

async openReports() { 
  const { ReportsComponent } = await import('./reports/reports.component'); 
  this.dynamicComponent = ReportsComponent; 
}

What happens:

  • When the user clicks the button, Angular calls the openReports() method.
  • The dynamic import('./reports/reports.component') is triggered, and the component file is requested only at that moment.
  • The browser downloads ReportsComponent as a separate JavaScript bundle (chunk).
  • Once loaded, the component is assigned to this.dynamicComponent.
  • Using <ng-container *ngComponentOutlet="dynamicComponent">, the component is dynamically rendered in the DOM.

The component is not part of the initial bundle; it loads only when needed, which improves performance.

5. Deferred Loading with (@defer) – Angular 16+

Angular 16 introduced a simpler method. Using the new @defer block makes lazy loading even easier. Instead of writing extra routing or dynamic import code, you can directly tell Angular when a component should load inside your template.

Instead of writing import logic manually, you can use:

App.component.html

@defer (on interaction) { 
  <app-reports></app-reports> 
} @placeholder { 
  <p>Loading reports...</p> 
}

How It Works:

The @defer block tells Angular not to load a component immediately. Instead, Angular splits that component into a separate JavaScript chunk during build time. This chunk is excluded from the main bundle, which keeps the initial app load smaller and faster.

When the specified trigger (for example, on interaction) occurs:

  1. Angular detects the trigger event.
  2. The browser downloads the deferred component’s JavaScript chunk.
  3. Angular initializes and renders the component.
  4. The @placeholder content is automatically replaced with the actual component.

Until the trigger fires, the component:

  • Is not downloaded
  • Is not parsed
  • Is not added to the DOM

This reduces startup time, improves perceived performance, and keeps heavy UI sections from slowing down the first screen.

Performance Visualization (Conceptual)

Without Lazy Loading

Main Bundle (Very Large)
├── Admin
├── Reports
├── Profile
├── Settings
└── Everything Else

With Lazy Loading

Main Bundle (Small)
├── Core
├── Home

Lazy Chunks
├── Admin
├── Reports
├── Profile

Result → Faster First Paint + Better User Experience

Benefits of using Lazy Loading:

Faster Startup: Only the required components are loaded upfront, which reduces the startup time and gives users a quicker first experience.

Efficient Resource Usage: Since unused modules aren’t loaded right away, the app consumes less memory and runs more smoothly.

Clean & Scalable Structure: Encourages a modular structure, making the codebase easier to maintain and scale as the application grows.

Final Takeaway

If your Angular app feels slow at startup, lazy loading is the first optimization you should implement.

Start with:

  1. Converting feature modules to lazy-loaded modules
  2. Measuring bundle size improvements
  3. Adding preloading only if needed
  4. Using @defer for heavy UI sections

Small architectural changes can dramatically improve performance.