Lazy loading with Angular

Updated on: 03/17/2025 Danny
Version Française

In this tutorial, we will implement Lazy Loading to improve the performance of our Angular 19.2.2 Web Application


Why use Lazy Loading?

  • Speed up initial application loading.

  • Load only the features you need, on demand.

  • Optimize the user experience with smoother navigation.
intro alt 00001
guide alt 00001

If you don't have time to read this entire guide,
download it now


What are we going to do?

We are going to configure Lazy loading in our Web Application with Angular version 19.2.2

For this we will use an existing project whose characteristics are

  • Generated with Angular CLI
  • Routing

All created sources are indicated at the end of the tutorial.

The application is at the following address


Before you start

The speed at which a website is displayed is one of the most essential criteria for the user.
And this speed is measured in seconds.

Beyond 3 seconds, 57% of users simply leave the site.

Everything and everything right away

Everything and everything right away

Because it's a matter of life and death.

So what methods or techniques should we use to make our website load quickly?

One technique is lazy loading .

It has the effect of speeding up the operation of a website.
It allows you to specify which parts of a website should be loaded when it starts.


Project creation

Rather than recreating everything we will use a project that contains the routing.
​​​​​​​I'll give you the commands to run, Git is obviously required to retrieve the source code.

# Create a demo directory (the name is arbitrary here)
mkdir demo

# Go to this directory
cd demo

# Get the source code on your workstation
git clone https://github.com/ganatan/angular-react-routing

# Go to the directory that was created
cd angular-react-routing
cd frontend-angular

# Run the dependency (or library) installation
npm install

# Run the program
npm run start

# Check that it works by running the command in your browser
http://localhost:4200/

Theory

Before going any further we need to understand how Angular works.
The command we are interested in concerns the compilation of our project.

In our package.json file this is the command

  • ng build

Without going into details this command uses esbuild (a bundler module).
Thanks to esbuild, angular uses the files of our project, compiles them to generate in the dist directory a certain number of files that we can deploy on a web server.

The project we are using as a basis has 6 web pages

  • Home
  • About
  • Contact
  • Login
  • Signup
  • not found


Compiling our source code generates a main.js file which contains the code for these 6 pages (or a file of the type main.xxxxxxxx.js )

To verify this theory, simply open the file dist/angular-starter/browser/main.xxxxxxxx.js and search for the code used in each of the 6 pages.

  • home works! (code used in home.component.html)
  • not-found works! (code used in not-found.component.html)
  • contact works! (code used in contact.component.html)
  • login works! (code used in login.component.html)
  • signup works! (code used in signup.component.html)
  • about works! (code used in about.component.html)

This file and others will be called when viewing the website.
The greater the number of pages, the larger the file will be and the slower the display will be.

The principle of lazy loading consists of splitting this file into several parts which will only be loaded when required.

So let's move on to practice.


Practical

Since the implementation of standalone components, lazy loading management has been simplified.

We will use the Angular documentation to apply this technique.
https://angular.dev/guide/routing/common-router-tasks#lazy-loading

We will adapt our architecture by modifying the file that manages routing.
In our example the app.routes.ts file

app.routes.ts before lazy loading
import { Routes } from '@angular/router';

import { HomeComponent } from './features/home/home.component';
import { LoginComponent } from './features/login/login.component';
import { SignupComponent } from './features/signup/signup.component';
import { NotFoundComponent } from './features/not-found/not-found.component';
import { AboutComponent } from './features/about/about.component';
import { ContactComponent } from './features/contact/contact.component';

import { ExperienceComponent } from './features/about/experience/experience.component';
import { SkillComponent } from './features/about/skill/skill.component';

import { MailingComponent } from './features/contact/mailing/mailing.component';
import { MappingComponent } from './features/contact/mapping/mapping.component';
import { WebsiteComponent } from './features/contact/website/website.component';

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

  { path: 'login', component: LoginComponent },
  { path: 'signup', component: SignupComponent },

  {
    path: 'about', component: AboutComponent,
    children: [
      { path: '', component: ExperienceComponent },
      { path: 'experience', component: ExperienceComponent },
      { path: 'skill', component: SkillComponent },
    ],
  },
  {
    path: 'contact', component: ContactComponent,
    children: [
      { path: '', component: MailingComponent },
      { path: 'mailing', component: MailingComponent },
      { path: 'mapping', component: MappingComponent },
      { path: 'website', component: WebsiteComponent },
    ],
  },

  { path: '**', component: NotFoundComponent }
];

For example for the LoginComponent component.

The loading was done this way.
path: 'login', component: LoginComponent

The change will be simple
loadComponent: () => import(`./features/login/login.component`)

I give you the complete file

src/app/app-routes.ts after Lazy Loading
import { Routes } from '@angular/router';

import { HomeComponent } from './features/home/home.component';
import { NotFoundComponent } from './features/not-found/not-found.component';
import { AboutComponent } from './features/about/about.component';
import { ContactComponent } from './features/contact/contact.component';

import { ExperienceComponent } from './features/about/experience/experience.component';
import { SkillComponent } from './features/about/skill/skill.component';

import { MailingComponent } from './features/contact/mailing/mailing.component';
import { MappingComponent } from './features/contact/mapping/mapping.component';
import { WebsiteComponent } from './features/contact/website/website.component';

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

  {
    path: 'login',
    loadComponent: () => import(`./features/login/login.component`)
      .then(mod => mod.LoginComponent)
  },
  {
    path: 'signup',
    loadComponent: () => import(`./features/signup/signup.component`)
      .then(mod => mod.SignupComponent)
  },

  {
    path: 'about', component: AboutComponent,
    children: [
      { path: '', component: ExperienceComponent },
      { path: 'experience', component: ExperienceComponent },
      { path: 'skill', component: SkillComponent },
    ],
  },
  {
    path: 'contact', component: ContactComponent,
    children: [
      { path: '', component: MailingComponent },
      { path: 'mailing', component: MailingComponent },
      { path: 'mapping', component: MappingComponent },
      { path: 'website', component: WebsiteComponent },
    ],
  },

  { path: '**', component: NotFoundComponent }
];

Verification

To verify the lazy loading theory we need to perform a new build (npm run build)

In the dist/angular-starter directory we get this time several files in addition to the main.js file

  • main.js
  • chunk-xxx1.js
  • chunk-xxx2.js
  • chunk-xxx3.js
  • chunk-xxx4.js

Noticed:
​​​​​​​The names can be different, especially with numbers; it is webpack that manages the naming.

The code for each of our pages is now arranged as follows

  • home works! (code found in main.js )
  • not-found works! (code found in main.js )
  • about works! (code used main.js )
  • contact works! (code found in chunk-xxx1.js )
  • login works! (code found in chunk-xxx2.js )
  • signup works! (code found in chunk-xxx3.js )

If we run the application (npm run start) we can see in Chrome (F12) in the Network tab how the files are loaded.

  • When the site launches: main.js is called.
  • On login selection: chunk-xxx2.js is called only once
  • At signup selection: chunk-xxx3.js is called only once
  • When selecting Contact: chunk-xxx4.js is called only once


If we run the url localhost/contact

  • In this case main.js and only chunk-xxx1.js are called


Conclusion :
Regardless of the number of pages, the main.js file will always be the same size.
The site that loads the main.js file will always launch at the same speed.


Child Routes

This application also includes Child routes management.

The different keywords that could refer to it are as follows:

  • routing
  • under routing
  • subrouting
  • nested routes
  • children routes

This question is raised in the documentation
https://angular.io/guide/router#child-route-configuration

You will find in the repository on github the addition of the notion of routing with Children.

In the routing tutorial three components were added in Contact

  • mailing
  • mapping
  • ​​​​​​​website

To be able to load them with lazy loading.
We just need to create two additional files that will be used later in app.routes.ts

  • about.config.ts
  • about.routes.ts

And I add those for you for contact.

src/app/features/about/about.routes.ts
import { Routes } from '@angular/router';

import { AboutComponent } from './about.component';

export const routes: Routes = [
  {
    path: '', component: AboutComponent, children: [
      {
        path: '',
        loadComponent: () => import(`./experience/experience.component`)
          .then(mod => mod.ExperienceComponent)
      },
      {
        path: 'experience',
        loadComponent: () => import(`./experience/experience.component`)
          .then(mod => mod.ExperienceComponent)
      },
      {
        path: 'skill',
        loadComponent: () => import(`./skill/skill.component`)
          .then(mod => mod.SkillComponent)
      },

      {
        path: '**',
        loadComponent: () => import(`./experience/experience.component`)
          .then(mod => mod.ExperienceComponent)
      },

    ]
  },
];
src/app/features/about/about.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './about.routes';

export const aboutConfig: ApplicationConfig = {
  providers: [provideRouter(routes)]
};
src/app/features/contact/contact.routes.ts
import { Routes } from '@angular/router';

import { ContactComponent } from './contact.component';

export const routes: Routes = [
  {
    path: '', component: ContactComponent, children: [
      {
        path: '',
        loadComponent: () => import(`./mailing/mailing.component`)
          .then(mod => mod.MailingComponent)
      },
      {
        path: 'mapping',
        loadComponent: () => import(`./mapping/mapping.component`)
          .then(mod => mod.MappingComponent)
      },
      {
        path: 'website',
        loadComponent: () => import(`./website/website.component`)
          .then(mod => mod.WebsiteComponent)
      },

      {
        path: '**',
        loadComponent: () => import(`./mailing/mailing.component`)
          .then(mod => mod.MailingComponent)
      },

    ]
  },
];
src/app/features/contact/contact.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './contact.routes';

export const contactConfig: ApplicationConfig = {
  providers: [provideRouter(routes)]
};
src/app/app-routes.ts
import { Routes } from '@angular/router';

import { HomeComponent } from './features/home/home.component';
import { NotFoundComponent } from './features/not-found/not-found.component';

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

  {
    path: 'login',
    loadComponent: () => import(`./features/login/login.component`)
      .then(mod => mod.LoginComponent)
  },
  {
    path: 'signup',
    loadComponent: () => import(`./features/signup/signup.component`)
      .then(mod => mod.SignupComponent)
  },
  
  {
    path: 'contact',
    loadChildren: () => import(`./features/contact/contact.routes`)
      .then(routes => routes.routes)
  },

  {
    path: 'about',
    loadChildren: () => import('./features/about/about.routes')
    .then(routes => routes.routes)
  },

  { path: '**', component: NotFoundComponent }
];

Tests

With standalones, test files do not require modifications.
All that remains is to test the application.

# Development
npm run start
http://localhost:4200/

# Test
npm run lint
npm run test

# Production
npm run build

Code source

The source code used at the beginning of the tutorial is available on github
https://github.com/ganatan/angular-react-routing

The source code obtained at the end of this tutorial is available on github
https://github.com/ganatan/angular-react-lazy-loading


If the code works and if you like why not give it a star on github

In any case, one of the two had a star (or not)

Marvel or DC

Marvel or DC

How to create a From scratch application?

Create your ganatan account

Download your complete guides for free

Démarrez avec angular CLI Démarrez avec angular CLI

Gérez le routing Gérez le routing

Appliquez le Lazy loading Appliquez le Lazy loading

Intégrez Bootstrap Intégrez Bootstrap


Utilisez Python avec Angular Utilisez Python avec Angular

Utilisez Django avec Angular Utilisez Django avec Angular

Utilisez Flask avec Angular Utilisez Flask avec Angular

Ganatan site Web avec Angular