Setting Up Lazy Loading in Angular

Lazy loading is a design pattern in Angular that allows modules to be loaded on demand rather than at the application startup. This approach reduces the initial bundle size, improves application performance, and optimizes resource usage for large applications. In this post, we will go through step-by-step instructions for setting up lazy loading in Angular, including code examples and best practices.

1. Introduction to Lazy Loading

Angular applications often consist of multiple feature modules. By default, all modules are loaded when the application starts, which can increase the initial load time. Lazy loading allows developers to load feature modules only when the user navigates to a route associated with that module.

Benefits of lazy loading:

  • Faster initial load times.
  • Improved performance for large applications.
  • Efficient use of bandwidth.
  • Better modularity and separation of concerns.

Lazy loading is achieved in Angular using the loadChildren property in routing configuration.

2. Prerequisites

Before implementing lazy loading, make sure:

  • Angular CLI is installed.
  • Your application is structured into feature modules.
  • You are familiar with Angular routing and NgModule.

3. Step 1: Create a Feature Module with Routing

To implement lazy loading, start by creating a feature module. You can do this using the Angular CLI:

ng generate module admin --route admin --module app.module

Explanation:

  • admin is the feature module.
  • --route admin tells Angular to automatically set up routing for this module.
  • --module app.module registers the module in the main application routing.

Alternatively, you can manually create a module and routing module:

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

@NgModule({
  declarations: [AdminComponent],
  imports: [CommonModule, AdminRoutingModule]
})
export class AdminModule {}
// admin-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminComponent } from './admin.component';

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

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

Here, AdminModule is a feature module that contains its own routing.


4. Step 2: Remove Feature Module from AppModule Imports

If you previously imported the AdminModule in AppModule, remove it. Importing it in AppModule would bundle it into the main application, defeating the purpose of lazy loading.

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [
BrowserModule,
AppRoutingModule, // Feature modules will be lazy loaded here
], providers: [], bootstrap: [AppComponent] }) export class AppModule {}

5. Step 3: Configure Lazy Loading in Main Routing File

In the main routing module (app-routing.module.ts), configure lazy loading using loadChildren:

// 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' }, { path: '**', redirectTo: 'home' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}

Explanation:

  • The loadChildren function dynamically imports the module only when the user navigates to /admin.
  • This ensures the module is not included in the initial bundle.
  • Wildcard and redirect routes handle invalid URLs and default paths.

6. How Angular Loads Modules Lazily

When a user navigates to /admin:

  1. Angular fetches the module’s JavaScript bundle.
  2. The module is instantiated.
  3. Angular renders the associated component(s).

Until the user navigates, the module code does not exist in memory, saving resources.


7. Nested Lazy Loading

Angular allows nested lazy-loaded modules. For example, you could have an AdminSettingsModule loaded only when /admin/settings is visited:

// admin-routing.module.ts
const routes: Routes = [
  { path: '', component: AdminComponent },
  {
path: 'settings',
loadChildren: () => import('./settings/settings.module').then(m => m.SettingsModule)
} ];

8. Preloading Strategies

Lazy loading reduces initial load time, but sometimes it’s beneficial to preload certain modules after the application starts. Angular provides PreloadAllModules strategy:

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

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

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

This approach lazy loads modules but preloads them in the background, improving navigation performance.


9. Route Guards with Lazy Loading

You can protect lazy-loaded modules using route guards such as CanLoad:

// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanLoad, Route, UrlSegment, Router } from '@angular/router';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanLoad {
  canLoad(route: Route, segments: UrlSegment[]): boolean {
const isAuthenticated = !!localStorage.getItem('token');
if (!isAuthenticated) {
  this.router.navigate(['/login']);
}
return isAuthenticated;
} constructor(private router: Router) {} }

Apply the guard to the lazy-loaded route:

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

10. Lazy Loading Best Practices

  • Modularize Features: Create separate modules for each feature.
  • Use Routing: Always combine feature modules with routing.
  • Avoid Eager Imports: Do not import lazy modules in AppModule.
  • Guard Sensitive Routes: Use CanLoad or CanActivate for restricted access.
  • Preload When Needed: Use PreloadAllModules for critical features.
  • Optimize Bundle Size: Keep modules small to maximize the benefit of lazy loading.

11. Debugging Lazy Loading

  • Check Network Tab: Ensure the feature module is only loaded on route access.
  • Look for Errors: Missing RouterModule.forChild or wrong paths can cause errors.
  • Verify Guards: Ensure CanLoad does not block legitimate access unintentionally.

12. Summary

Lazy loading is essential for large Angular applications to improve performance and user experience. Steps:

  1. Create a feature module with its own routing.
  2. Remove the module from AppModule imports.
  3. Configure lazy loading in AppRoutingModule using loadChildren.

Optional improvements:

  • Use nested lazy loading for sub-features.
  • Implement preloading strategies.
  • Protect modules with route guards.

By following these practices, Angular applications can scale efficiently without compromising on performance.


Example Complete Code

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

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

// admin-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminComponent } from './admin.component';

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

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

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes, PreloadAllModules } from '@angular/router';

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

This setup ensures AdminModule is loaded only when required, optimizing your Angular application.


Comments

Leave a Reply

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