Angular Lazy Loading

21 Jun 202516 minutes to read

Lazy loading is an optimization technique used in Angular applications to improve performance by loading only the necessary modules on demand. This section provides a detailed guide on implementing lazy loading with Essential® JS2 Angular components, including Syncfusion® controls. Lazy loading is instrumental in minimizing the initial loading time, thereby enhancing user experience significantly.

Lazy Loading

Lazy loading is a technique that loads additional payload only when needed, which can improve the overall performance and user experience of your Angular application. By using code splitting, you can lazy load the Syncfusion® components and routes in Angular. This can reduce the initial loading time of the application.

Folder Structure Overview

src/
├── app/
   ├── home/           # Lazy-loaded feature module with Syncfusion Grid
   ├── about/          # Lazy-loaded feature module with Syncfusion Dropdown
   ├── app-routing.module.ts # Root routing config with lazy loading
   └── app.component.ts      # Root component with navigation

Creating a Syncfusion® component in Angular

Begin developing your Angular application with Syncfusion® components by following the getting started guide. Additionally, refer to the Angular lazy-loading documentation for a comprehensive understanding of implementing lazy loading.

Project Setup

1. Create App & Install Syncfusion® components

Run these commands to create a standalone Angular project and install Syncfusion® components:

ng new ngmodule-lazy-demo --routing --style=css
cd ngmodule-lazy-demo
npm install @syncfusion/ej2-angular-grids @syncfusion/ej2-angular-dropdowns
npm install @syncfusion/ej2-angular-buttons

Step 2: Generate Feature Modules

Run the following commands to generate feature modules for home and about. These modules will include routing configuration.

ng generate module home --route home --module app.module
ng generate module about --route about --module app.module

Add the css in the styles.css

@import '../node_modules/@syncfusion/ej2-base/styles/material.css';  
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';  
@import '../node_modules/@syncfusion/ej2-calendars/styles/material.css';  
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css';  
@import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';  
@import '../node_modules/@syncfusion/ej2-navigations/styles/material.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-notifications/styles/material.css';
@import '../node_modules/@syncfusion/ej2-angular-grids/styles/material.css';
@import '../node_modules/@syncfusion/ej2-base/styles/material.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';

Home Module - Displays Grid with local data

home.component.ts

Displays a simple Syncfusion® Grid using local in-memory data.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-home',
  template: `
    <h2>🏠 Home Page with Syncfusion Grid</h2>
    <ejs-grid [dataSource]="data">
      <e-columns>
        <e-column field="id" headerText="ID" width="100" textAlign="Right"></e-column>
        <e-column field="name" headerText="Name" width="150"></e-column>
        <e-column field="role" headerText="Role" width="150"></e-column>
      </e-columns>
    </ejs-grid>
  `
})
export class HomeComponent implements OnInit {
  public data = [
    { id: 1, name: 'Alice', role: 'Admin' },
    { id: 2, name: 'Bob', role: 'User' },
    { id: 3, name: 'Carol', role: 'Moderator' }
  ];

  ngOnInit() {}
}

Home Module - Lazy-loaded module that sets up routing and imports Syncfusion® Grid

home.module.ts

Lazy-loaded module that sets up routing and imports Syncfusion® Grid.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HomeComponent } from './home.component';
import { RouterModule, Routes } from '@angular/router';
import { GridModule } from '@syncfusion/ej2-angular-grids';

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

@NgModule({
  declarations: [HomeComponent],
  imports: [
    CommonModule,
    GridModule,
    RouterModule.forChild(routes)
  ]
})
export class HomeModule {}

About Module - Displays Dropdown List

about.component.ts

Displays a Syncfusion® Dropdown List with a simple string array.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-about',
  template: `
    <h2>ℹ️ About Page with Syncfusion Dropdown</h2>
    <ejs-dropdownlist [dataSource]='languages' placeholder='Select a language'></ejs-dropdownlist>
    <br/><br/>
  `
})
export class AboutComponent implements OnInit {
  public languages: string[] = ['JavaScript', 'TypeScript', 'Python', 'C#'];

  ngOnInit() {}
}

About Module - Lazy-loaded module that configures the route and imports Syncfusion® Dropdown

about.module.ts

Lazy-loaded module that configures the route and imports Syncfusion® Dropdown.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AboutComponent } from './about.component';
import { RouterModule, Routes } from '@angular/router';
import { DropDownListModule } from '@syncfusion/ej2-angular-dropdowns';

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

@NgModule({
  declarations: [AboutComponent],
  imports: [
    CommonModule,
    DropDownListModule,
    RouterModule.forChild(routes)
  ]
})
export class AboutModule {}

App Routing - Connects lazy-loaded routes

Configure app-routing.module.ts for lazy loading by dynamically importing the required modules

app-routing.module.ts

Defines route-level code-splitting using loadChildren for Home and About modules.

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

const routes: Routes = [
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then(m => m.HomeModule)
  },
  {
    path: 'about',
    loadChildren: () => import('./about/about.module').then(m => m.AboutModule)
  },
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full'
  }
];

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

In the above code block, we are using loadChildren property in the routing configuration to lazily load the HomeModule and AboutModule when the user navigates to the corresponding routes. This way, the application only loads the necessary modules on demand, improving the initial loading time and performance of the application.

app.module.ts

The app.module.ts file in an Angular application holds the following key elements:

  • Angular built-in modules (like BrowserModule, FormsModule, HttpClientModule, etc.).

  • Third-party libraries or feature-specific modules.

  • Custom or feature modules created in the app.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { GridModule, PagerModule } from '@syncfusion/ej2-angular-grids';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    GridModule, PagerModule,ButtonModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

The app.component.ts file in an Angular application is the root component of the app, typically responsible for controlling the main view or layout of the application.

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <h1>Angular Standalone Lazy Loading</h1>
  <div class="e-section-control" style="text-align: center; margin: 20px 0;">
  <button ejs-button class="e-primary" routerLink="/home">Home</button>
  <button ejs-button class="e-success" routerLink="/about" style="margin-left: 15px;">About</button>
</div>
    <router-outlet></router-outlet>
  `,
})
export class AppComponent { }

Lazy loading using standalone® application

This update enhances the previous documentation, focusing on Angular 17’s latest features and improvements for building a standalone application with lazy loading.

Standalone Components Enhancements

Angular 17 introduces the standalone component—a component not part of any ngModule that can be used with either other standalone or module-based components.

In the past, a lazy route pointed to an NgModule with child routes. As there are no NgModules anymore, loadChildren can now directly point to a lazy routing configuration:

Angular Lazy Loading using Standalone Components

This guide demonstrates lazy loading using Angular’s modern standalone components without NgModules. This approach improves maintainability and embraces Angular 17+ best practices. Each route lazily loads a feature using Syncfusion® UI components and local data.

Folder Structure Overview

src/
├── app/
│   ├── home/                  # Home standalone component with Syncfusion Grid
│   │   └── home.routes.ts     # Lazy routing definition for Home
│   ├── about/                 # About standalone component with Dropdown
│   │   └── about.routes.ts    # Lazy routing definition for About
│   ├── app.routes.ts          # Root route configuration with lazy loading
│   └── app.component.ts       # Root component with router navigation

Project Setup

  1. Create App & Install Syncfusion
    Run the following commands to create a new Angular standalone project and install Syncfusion® components:
ng new standalone-lazy-demo --routing --standalone --style=css
cd standalone-lazy-demo
npm install @syncfusion/ej2-angular-grids @syncfusion/ej2-angular-dropdowns
npm install @syncfusion/ej2-angular-buttons

Add the style in styles.css file

@import '../node_modules/@syncfusion/ej2-base/styles/material.css';  
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';  
@import '../node_modules/@syncfusion/ej2-calendars/styles/material.css';  
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css';  
@import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';  
@import '../node_modules/@syncfusion/ej2-navigations/styles/material.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-notifications/styles/material.css';
@import '../node_modules/@syncfusion/ej2-angular-grids/styles/material.css';
@import '../node_modules/@syncfusion/ej2-base/styles/material.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';

Step 2: Generate the home Component

Run the following command to generate the home component as a standalone component, which will not generate module files:

ng generate component home --standalone --skip-tests

Generate the about Component

Run the following command to generate the about component as a standalone component, which will not generate module files:

ng generate component about --standalone --skip-tests

Home Component - Displays Grid

home.component.ts

Standalone component that displays Syncfusion® Grid with local static data.

import { Component } from '@angular/core';
import { GridModule } from '@syncfusion/ej2-angular-grids';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-home',
  standalone: true,
  imports: [CommonModule, GridModule],
  template: `
    <h2>Home Page</h2>
    <ejs-grid [dataSource]="orders">
      <e-columns>
        <e-column field="OrderID" headerText="Order ID" textAlign="Right" width="120"></e-column>
        <e-column field="CustomerID" headerText="Customer ID" width="150"></e-column>
        <e-column field="ShipCity" headerText="Ship City" width="150"></e-column>
        <e-column field="ShipName" headerText="Ship Name" width="150"></e-column>
      </e-columns>
    </ejs-grid>
  `
})
export class HomeComponent {
  public orders = [
    { OrderID: 10248, CustomerID: 'VINET', ShipCity: 'Reims', ShipName: 'Vins et alcools Chevalier' },
    { OrderID: 10249, CustomerID: 'TOMSP', ShipCity: 'Münster', ShipName: 'Toms Spezialitäten' },
    { OrderID: 10250, CustomerID: 'HANAR', ShipCity: 'Rio de Janeiro', ShipName: 'Hanari Carnes' }
  ];
}

About Component - Displays Dropdown

about.component.ts

Standalone component using Syncfusion® Dropdown List with string array as options.

import { Component } from '@angular/core';
import { DropDownListModule } from '@syncfusion/ej2-angular-dropdowns';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-about',
  standalone: true,
  imports: [CommonModule, DropDownListModule],
  template: `
    <h2>About Page</h2>
    <h5>Having Dropdown Here</h5>
    <ejs-dropdownlist [dataSource]="items" placeholder="Select an item"></ejs-dropdownlist>
  `
})
export class AboutComponent {
  public items: string[] = ['Option 1', 'Option 2', 'Option 3'];
}

Manually Creating the Route File for Standalone Components

Unfortunately, by default, Angular CLI doesn’t generate a route.ts file for routing when you create a standalone component using ng generate component.
You can manually create a corresponding home.routes.ts and about.routes.ts file for routing

Route Definitions

home.routes.ts

Defines the lazy route for the HomeComponent.

import { Routes } from '@angular/router';
import { HomeComponent } from './home.component';

export default [
  { path: '', component: HomeComponent }
] as Routes;

about.routes.ts

Defines the lazy route for the AboutComponent.

import { Routes } from '@angular/router';
import { AboutComponent } from './about.component';

export default [
  { path: '', component: AboutComponent }
] as Routes;

App Routing - Lazy Loading via loadChildren

app.routes.ts

Main route configuration using dynamic imports for lazy loading each route.

import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: 'home',
    loadChildren: () => import('./home/home.routes')
  },
  {
    path: 'about',
    loadChildren: () => import('./about/about.routes')
  },
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full'
  }
];

App Component - Navigation Setup

app.component.ts

Displays links to lazy-loaded routes and handles route view display.

import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterModule, ButtonModule],
  template: `
    <h1>Angular Standalone Lazy Loading</h1>
  <div class="e-section-control" style="text-align: center; margin: 20px 0;">
  <button ejs-button class="e-primary" routerLink="/home">Home</button>
  <button ejs-button class="e-success" routerLink="/about" style="margin-left: 15px;">About</button>
</div>
    <router-outlet></router-outlet>
  `,
})
export class AppComponent { }

Run the App

ng serve

Routing With Standalone Component

With NgModules, each lazy module introduced a new injector and hence a new injection scope. This scope was used for providing services only needed by the respective lazy chunk.

To cover such use cases, the Router now allows for introducing providers for each route. These services can be used by the route in question and their child routes:

To ensure proper navigation between components in your Angular application, it is crucial to import the RouterModule in each of your component modules.

import { RouterModule } from '@angular/router';

// Other imports and declarations...

@component({
  imports: [
    // ... Other modules
    RouterModule, // Ensure RouterModule is imported
  ],
})

The RouterModule provides essential functionality for routing, allowing seamless navigation between different views.

Before Angular 17

  • NgModules were used to group all components, services, and other things.
  • When using lazy loading with NgModules, every lazy-loaded module had its own “scope” and injector. This scope was used to provide services only needed for that module.

What Changed in Angular 17

  • Angular 17 allows you to use standalone components, which don’t need NgModules.
  • You can still lazily load components, but now you can define services that are only available for the route that loads, instead of loading services for the entire module.

Troubleshooting

  • Grid or Dropdown Not Rendering: Ensure all required CSS files are imported in styles.css. Verify that the dataSource property in ejs-grid or ejs-dropdownlist is correctly set (e.g., not null or undefined).
  • Routing Errors: Confirm that RouterModule is imported in app.module.ts (for module-based) or app.component.ts (for standalone). Check that route paths in app-routing.module.ts or app.routes.ts match the routerLink values.
  • Syncfusion Component Errors: Ensure you have a valid Syncfusion license and that the correct Syncfusion packages are installed.

For additional help, refer to the Angular Documentation or Syncfusion Angular Documentation.

Benefits of Lazy Loading

  • Faster Initial Load: Lazy loading delays loading of non-essential components, reducing the initial bundle size. For example, a 500KB module can be deferred, cutting load time by up to 50% in apps with multiple features.
  • Simplified Structure (Standalone): Standalone components eliminate NgModule boilerplate, reducing project complexity and making maintenance easier.
  • Resource Efficiency: Only loads necessary code, saving memory and bandwidth, especially for users on slower networks.