What Is Lazy Loading in Angular

Lazy loading is one of the most powerful performance optimization techniques in Angular. It allows developers to load feature modules only when they are needed, instead of loading all modules at the initial application startup. This results in smaller bundle sizes, faster loading times, and improved overall performance — especially in large-scale applications.

In this post, we will explore the concept of lazy loading, how it works in Angular, how to configure it step-by-step, and best practices for implementing it effectively.

1. Introduction to Lazy Loading

When a user visits an Angular application, the browser downloads all JavaScript bundles necessary to run the app. If your application has multiple feature modules, this initial download can become very large and slow down performance.

Lazy loading solves this problem by splitting the app into smaller modules (called feature modules) that load only when the user navigates to a specific route.

This way, the user first downloads only what’s required for the initial screen, and the rest loads on demand.


2. Key Benefits of Lazy Loading

  1. Improved Performance
    • The initial load time of the app is reduced since only essential modules are loaded first.
  2. Optimized Resource Usage
    • Modules and components are loaded into memory only when required.
  3. Better User Experience
    • Faster initial interaction gives users a smoother and more responsive experience.
  4. Scalable Architecture
    • Large applications can be broken into multiple independently loadable modules.

3. How Lazy Loading Works

In Angular, lazy loading uses the Angular Router to dynamically import feature modules only when a route is activated.

The routing configuration defines which module should be loaded when a particular route is accessed.


4. Example of Lazy Loading

Below is a simple example of implementing lazy loading for an AdminModule.

app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
path: 'admin',
loadChildren: () =>
  import('./admin/admin.module').then(m => m.AdminModule)
}, {
path: '',
redirectTo: '/home',
pathMatch: 'full'
} ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}

Here, Angular will not load AdminModule when the app starts.
It will only load it when the user navigates to /admin.


5. Creating the Admin Module

admin.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminRoutingModule } from './admin-routing.module';
import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component';

@NgModule({
  declarations: [AdminDashboardComponent],
  imports: [CommonModule, AdminRoutingModule]
})
export class AdminModule {}

6. Setting Up the Admin Routing Module

admin-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component';

const routes: Routes = [
  { path: '', component: AdminDashboardComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class AdminRoutingModule {}

In this setup, when the user navigates to /admin, Angular dynamically loads the AdminModule, and then the AdminDashboardComponent is displayed.


7. The Difference Between Eager Loading and Lazy Loading

FeatureEager LoadingLazy Loading
When Modules LoadAt App StartupOn Route Access
Initial Load TimeSlowerFaster
Bundle SizeLargerSmaller
Use CaseSmall ApplicationsLarge Applications

8. Lazy Loading Multiple Feature Modules

You can easily configure multiple lazy-loaded modules in the same way.

Example:

const routes: Routes = [
  {
path: 'admin',
loadChildren: () =>
  import('./admin/admin.module').then(m => m.AdminModule)
}, {
path: 'user',
loadChildren: () =>
  import('./user/user.module').then(m => m.UserModule)
} ];

Both AdminModule and UserModule will load only when their respective routes are accessed.


9. Preloading Strategy

Preloading helps improve navigation performance by loading some lazy-loaded modules in the background after the initial load.

Example with PreloadAllModules

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

@NgModule({
  imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })],
  exports: [RouterModule]
})
export class AppRoutingModule {}

This tells Angular to preload all lazy-loaded modules after the app’s main bundle has loaded.


10. Selective Preloading Strategy

You can create a custom preloading strategy to control which modules should preload.

selective-preloading-strategy.service.ts

import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class SelectivePreloadingStrategy implements PreloadingStrategy {
  preload(route: Route, load: () => Observable<any>): Observable<any> {
return route.data &amp;&amp; route.data&#91;'preload'] ? load() : of(null);
} }

app-routing.module.ts

const routes: Routes = [
  {
path: 'admin',
loadChildren: () =&gt;
  import('./admin/admin.module').then(m =&gt; m.AdminModule),
data: { preload: true }
}, {
path: 'user',
loadChildren: () =&gt;
  import('./user/user.module').then(m =&gt; m.UserModule)
} ]; @NgModule({ imports: [RouterModule.forRoot(routes, { preloadingStrategy: SelectivePreloadingStrategy })], exports: [RouterModule] }) export class AppRoutingModule {}

Now only AdminModule will preload, while UserModule will load on demand.


11. Lazy Loading and Guards

You can apply Route Guards to lazy-loaded routes just like regular routes.

Example:

{
  path: 'admin',
  loadChildren: () =>
import('./admin/admin.module').then(m =&gt; m.AdminModule),
canLoad: [AuthGuard] }

Here, AuthGuard ensures that the module loads only if the user is authenticated.


12. Best Practices for Lazy Loading

  1. Organize Features in Modules
    • Group related components, services, and routes together in feature modules.
  2. Use Shared Modules
    • Put commonly used components, directives, and pipes in a SharedModule.
  3. Avoid Duplicate Services
    • Use providedIn: 'root' to prevent multiple instances of singleton services.
  4. Optimize Images and Assets
    • Large assets can still affect lazy loading performance. Optimize them.
  5. Test Navigation Flows
    • Ensure smooth transitions when modules load dynamically.

13. Debugging Lazy Loading

You can verify if lazy loading works properly by checking Network tab in the browser developer tools.
When navigating to a lazy-loaded route, you should see a new .js file being fetched dynamically.


14. Handling 404 Routes

You can add a wildcard route at the end to handle unknown URLs.

{ path: '**', redirectTo: '/home' }

15. Lazy Loading and Standalone Components (Angular 17+)

Angular now supports standalone components, which can also be lazy-loaded without a module.

Example:

const routes: Routes = [
  {
path: 'profile',
loadComponent: () =&gt;
  import('./profile/profile.component').then(c =&gt; c.ProfileComponent)
} ];

This approach eliminates the need for a separate module file, simplifying structure while maintaining lazy loading benefits.


16. Common Mistakes to Avoid

  1. Incorrect Path in loadChildren
    • Always use the correct relative import path.
  2. Missing Routing Module
    • Each feature module must have its own routing module.
  3. Importing Lazy Modules in AppModule
    • Never import lazy-loaded modules manually into AppModule.

17. Summary

Lazy loading is an essential Angular feature for creating performant, scalable, and maintainable applications.
By loading feature modules only when needed, you can drastically improve application startup time and user experience.

Key Takeaways:

  • Use loadChildren for route-based lazy loading.
  • Combine lazy loading with preloading strategies for efficiency.
  • Apply guards for security and conditional loading.
  • Leverage standalone components for simpler structures.

Example Recap

const routes: Routes = [
  {
path: 'admin',
loadChildren: () =&gt;
  import('./admin/admin.module').then(m =&gt; m.AdminModule)
} ];

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *