Learn About Lazy Loading In Angular

In this article, we are going to cover:
  • Lazy Loading of Feature Modules
  • Different Loading Strategies
  • Custom Preloading Strategy for Lazy-Loaded Modules

Lazy Loading of Feature Modules

Lazy loading is the process of loading some features of your Angular application only when you navigate to their routes for the first time! This can be useful for increasing your app performance and decreasing the initial size of the bundle that would be downloaded to a user’s browser.
Lazy loaded modules can have one or more components inside. But remember the module which you are going to lazy load needs to be a different module then your Root Module.
So, for example, if you want to lazy load a product component then you have to create a product module with all the components it needs, along with its own routing file and module file.
We all know lazy loading is one of the most useful concepts of Angular Routing so let us see how to implement it by this simple example.
Firstly, I have created a new blank Angular Application with Angular-CLI and then I have added 3 components to it; namely Home, Product and Employee. I have also created one component for Menu. As I have used Menu component in this example so I am using Router-Outlet to show the data of components which are going to be loaded outside that Menu area.
Okay, so below is the code for menu.component.html and app.component.html respectively.
  1. <ul class="nav nav-pills">  
  2.   <li class="nav-item">  
  3.     <a class="nav-link" routerLink ="/home" routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}">Home</a>  
  4.   </li>  
  5.   <li class="nav-item">  
  6.     <a class="nav-link" routerLink ="/employee" routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}">Employee List</a>  
  7.   </li>  
  8.   <li class="nav-item">  
  9.     <a class="nav-link" routerLink ="/product" routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}">Product List</a>  
  10.   </li>  
  11.   <li class="nav-item">  
  12.     <a class="nav-link" routerLink ="/product/add" routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}">Add Product</a>  
  13.   </li>  
  14. </ul>  
  1. <app-menu></app-menu>  
  2. <router-outlet></router-outlet>  
So to start with lazy loading, we go to the route configuration (app.routing.ts) and use the property loadChildren.
Let us see the syntax of loadChildren property,
  1. const arr: Routes = [  
  2. ....  
  3.   { path: 'employee', loadChildren: './employee/employee.module#EmployeeModule'}  
  4. ....  
  5. ];  
The loadChildren property accepts a string value which contains the route to your lazy-loaded module followed by a Hash symbol and then the class name of that module.
Check the whole app.routing.ts file as below,
  1. import { Routes, RouterModule } from '@angular/router';  
  2. import { HomeComponent } from './home/home.component';  
  4. const arr: Routes = [  
  5.   { path: '', redirectTo: '/home', pathMatch: 'full' },  
  6.   { path: 'home', component: HomeComponent},  
  7.   { path: 'product', loadChildren: './product/product.module#ProductModule' },  
  8.   { path: 'employee', loadChildren: './employee/employee.module#EmployeeModule'},  
  9.   { path: '**', component: HomeComponent }  
  10. ];  
  11. export const routingArr = RouterModule.forRoot(arr);  
Here, notice that we are not going to import those modules which we want to lazy load. In our case we want to lazy load Employee and Product modules so we do not write import statements for them.
So when the route gets activated, this loadChildren property will get activated and it will load the requested module by its given path. Then it will load the requested component and display that component’s template (Html).
Now the next task is to configure our Routes for these feature modules.
So, as I said earlier we need to create separate Routing file and Module file for each module whichever we want to lazy load.
So below is the code for employee.routing.ts file,
  1. import { Routes, RouterModule } from '@angular/router';  
  2. import { EmployeeComponent } from './employee.component';  
  4. const arr: Routes = [  
  5.   { path: '', component: EmployeeComponent }  
  6. ];  
  8. export const routingEmpArr = RouterModule.forChild(arr);  
So here we are actually giving a path to all the components of Employees Module created. This will actually find out the whole path of any component.
If you are having more than one component in one Module, Like Product Module in our example then it can be done as below,
  1. import { Routes, RouterModule } from '@angular/router';  
  2. import { ProductComponent } from './product.component';  
  3. import { AddproductComponent } from './addproduct/addproduct.component';  
  5. const arr: Routes = [  
  6.   { path: '', component: ProductComponent },  
  7.   { path: 'add', component: AddproductComponent }  
  8. ];  
  10. export const routingProductArr = RouterModule.forChild(arr);  
And finally create the Module files for each module. Here we need to create module files for both Employee and Product.
  1. import { NgModule } from '@angular/core';  
  2. import { CommonModule } from '@angular/common';  
  3. import { EmployeeComponent } from './employee.component';  
  4. import { routingEmpArr } from './employee.routing';  
  6. @NgModule({  
  7. declarations: [  
  8.   EmployeeComponent  
  9. ],  
  10. imports: [  
  11.   CommonModule,  
  12.   routingEmpArr  
  13. ]  
  14. })  
  16. export class EmployeeModule {}  
  1. import { NgModule } from '@angular/core';  
  2. import { CommonModule } from '@angular/common';  
  3. import { ProductComponent } from './product.component';  
  4. import { AddproductComponent } from './addproduct/addproduct.component';  
  5. import { routingProductArr } from './product.routing';  
  7. @NgModule({  
  8. declarations: [  
  9.   ProductComponent,  
  10.   AddproductComponent  
  11. ],  
  12. imports: [  
  13.   CommonModule,  
  14.   routingProductArr  
  15. ]  
  16. })  
  18. export class ProductModule {}  
So, the last and most important thing is to make changes in the app.module.ts.
Here in app.module.ts we will only load the Home Component. Because importing any component to this file means that it will load all these components when we run this application in the browser. But here we want some of the modules to be loaded only on demand or we can say when we navigate to those modules.
  1. import { BrowserModule } from '@angular/platform-browser';  
  2. import { NgModule } from '@angular/core';  
  3. import { routingArr } from './app.routing';  
  4. import { AppComponent } from './app.component';  
  5. import { MenuComponent } from './menu.component';  
  6. import { HomeComponent } from './home/home.component';  
  8. @NgModule({  
  9.   declarations: [  
  10.     AppComponent,  
  11.     HomeComponent,  
  12.     MenuComponent  
  13.   ],  
  14.   imports: [  
  15.     BrowserModule,  
  16.     routingArr  
  17.   ],  
  18.   providers: [],  
  19.   bootstrap: [AppComponent]  
  20. })  
  21. export class AppModule { }  
So in this way our lazy-loaded modules can be accessed.
Now, once we have lazy loaded, run this Application in the browser and go to the Network tab in Console to see which files are generated.
Lazy Loading In Angular
Here you can see no files regarding Employee and Product Modules are there. This means that when application gets loaded in the browser it will load only the necessary files it requires but now when we click on the Employee Menu then we can see the changes in Network tab as below,
Lazy Loading In Angular
As you can see a new bundle file (chunk) is generated for Employee. So this entire Employee module gets loaded in the browser when user navigates to that particular route for the first time. This is how lazy loading gets implemented using loadChildren property.
Similarly you can also check the Product module, 
Lazy Loading In Angular

Different Loading Strategies

Basically we can load any Module in three different ways,
  • Eagerly
  • Lazily
  • Preloading
Eager loading
By default every Angular Application uses this loading strategy. In Eager Loading all the feature modules are loaded before the application gets started along with the root (App) module. So if you are having small sized application then you can use this strategy because it will require all the modules and its dependencies to be ready when you are running the application for the first time.
To implement the Eager Loading, you have to import the modules in app.module.ts file just like what we do with any normal Angular Application.
Lazy Loading
As we have seen earlier if we use lazy loading then we can render the module on demand when needed. Also if the application size is becoming larger with many feature modules, loading everything eagerly will make the application slow. So instead we can use lazy loading.
By preloading, the application will start loading chunks of modules before needed. This means modules do not wait to get loaded until users navigate to their routes. Modules can be loaded in the background asynchronously just after the application gets started. 
For this strategy, Angular provides PreloadingStrategy abstract class. And it has two sub classes: PreloadAllModules and NoPreloading.
  • NoPreloading — default behaviour that doesn't preload any modules.
  • PreloadAllModules — all modules are preloaded as soon as possible.
To use any of the above strategies what you have to do is specify the strategy in your app-routing.module.ts as shown below,
  • import { RouterModule, Routes, PreloadAllModules } from '@angular/router';
  • Now inside @NgModule’s imports,
    1. imports: [  
    2.   ...  
    3.   RouterModule.forRoot(ROUTES,  
    4.   { preloadingStrategy: PreloadAllModules })  
    5. ],  
Let's see a quick example how we can use Preloading with Customization!

Custom Preloading Strategy for Lazy-Loaded Modules

If you do not want all lazy loadable modules to be preloaded then you can implement your own preloading strategy.
Ideally, we would like to preload the core features or most commonly used modules of our app. This will allow core features to load immediately as well as we will lazy load other features, which are less used, on demand when the user clicks the link.
So for this, we will define and export a CustomPreloading class that implements PreloadingStrategy class.
For Custom Preloading, I have created another fresh Angular Application which is having Dashboard, Product and Contact modules.
  1. import { Observable, of } from 'rxjs';  
  2. import { PreloadingStrategy, Route } from '@angular/router';  
  4. export class CustomPreloading implements PreloadingStrategy {  
  5.   preload(route: Route, preload: () => Observable<any>): Observable<any> {  
  6.     if (route.data && route.data.preload) {  
  7.       return preload();  
  8.     } else {  
  9.       return of(null);  
  10.     }  
  11.   }  
  12. }  
As you can see above we have implemented the PreloadingStrategy class so we need to override the Preload() method of that particular class. This method receives the active route as a parameter. With this route, it will look at whether the Data object was set or not. If set, then it will load that module otherwise it returns null which means it will not preload.
Now in our app.module.ts, we will import the PreloadingStrategy class, and define that the RouterModule should use the CustomPreLoading as our pre loading strategy. After that in providers array we will provide the path of this custom preloading file.
  1. import { BrowserModule } from '@angular/platform-browser';  
  2. import { NgModule } from '@angular/core';  
  3. import { AppComponent } from './app.component';  
  4. import { routes } from './app.routing';  
  5. import { RouterModule } from '@angular/router';  
  6. import { MenuComponent } from './menu.component';  
  7. import { CustomPreloading } from './custom-preloading';  
  8. import { DashboardComponent } from './dashboard/dashboard.component';  
  10. @NgModule({  
  11.   declarations: [  
  12.     AppComponent,  
  13.     MenuComponent,  
  14.     DashboardComponent  
  15.   ],  
  16.   imports: [  
  17.     BrowserModule,  
  18.     RouterModule.forRoot( routes, { preloadingStrategy: CustomPreloading}),  
  20.   ],  
  21.   providers: [ CustomPreloading ],  
  22.   bootstrap: [AppComponent]  
  23. })  
  24. export class AppModule { }  
Now it’s time to set the Routes.
For customized pre loading, we have to specify the Data object to the path that we define. This Data object is having property preload which should be set to true. So, the syntax is: data: {preload: true}
So, according to our CustomPreloading strategy implementation, if the value of data.preload:true then we are confirming that module is to be preloaded and if the preload value is not true then the module won’t be preloaded.
  1. import { Routes } from '@angular/router';  
  2. import { DashboardComponent } from './dashboard/dashboard.component';  
  4. export const routes: Routes = [  
  6.   // Dashboard is Eager loaded  
  7.   { path: '', component: DashboardComponent, pathMatch: 'full' },  
  8.   { path: 'dashboard', component: DashboardComponent},  
  10.   // Contact will be Preloaded  
  11.   { path: 'contact', loadChildren: './contact/contact.module#ContactModule', data: {preload: true}},  
  13.   // Product will be Lazy loaded  
  14.   { path: 'product', loadChildren: './product/product.module#ProductModule'},  
  16.   { path: '**', component: DashboardComponent }  
  17. ];  
Okay, so finally run the application and check the Console’s Network tab.
Here notice that if you are loading the app for the first time, it will load both  the Dashboard module and Contact Module. Because we have preloaded Contact Module so its chunk gets loaded initially.
Lazy Loading In Angular
And now click on the Product Menu. And you will notice that the chunk for that get loaded at runtime when you actually visit that route.
Lazy Loading In Angular
Thank you for reading!
You can find the full code for this here,