What is Route Lazy Loading in Angular?
What is Route Lazy Loading?
Route lazy loading is a technique where Angular loads a component or feature only when the user navigates to its route, instead of loading everything when the application initially starts.
For example, suppose an application contains:
Home
Products
Orders
Admin
Reports
Without lazy loading:
Application starts
↓
Home code loaded
Products code loaded
Orders code loaded
Admin code loaded
Reports code loaded
With lazy loading:
Application starts
↓
Only initial required code loaded
User visits /products
↓
Products code loaded
User visits /admin
↓
Admin code loaded
This helps reduce the initial JavaScript bundle and startup work, especially in larger applications.
Full Simple Sample Program
In modern standalone Angular, a common lazy-loading approach is loadComponent().
home.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
standalone: true,
template: `
<h2>Home Page</h2>
<p>Welcome to the application.</p>
`
})
export class Home {
}
products.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-products',
standalone: true,
template: `
<h2>Products Page</h2>
<ul>
<li>Laptop</li>
<li>Mobile</li>
<li>Keyboard</li>
</ul>
`
})
export class Products {
}
app.routes.ts
import { Routes } from '@angular/router';
import { Home } from './home';
export const routes: Routes = [
{
path: '',
component: Home
},
{
path: 'products',
loadComponent: () =>
import('./products')
.then(m => m.Products)
}
];
app.ts
import { Component } from '@angular/core';
import { RouterLink, RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [
RouterLink,
RouterOutlet
],
template: `
<h1>My Angular Application</h1>
<nav>
<a routerLink="/">Home</a> |
<a routerLink="/products">Products</a>
</nav>
<hr>
<router-outlet></router-outlet>
`
})
export class App {
}
app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes)
]
};
main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { App } from './app/app';
import { appConfig } from './app/app.config';
bootstrapApplication(App, appConfig)
.catch(err => console.error(err));
Explanation
The important code is:
{
path: 'products',
loadComponent: () =>
import('./products')
.then(m => m.Products)
}
Notice that we did not statically import Products at the top of app.routes.ts.
We didn't write:
import { Products } from './products';
Instead, we use dynamic import():
import('./products')
This allows the build system to split that code so it can be loaded when needed.
component vs loadComponent
Normal/Eager Route
import { Products } from './products';
{
path: 'products',
component: Products
}
The component is statically referenced by the route configuration.
Lazy Route
{
path: 'products',
loadComponent: () =>
import('./products')
.then(m => m.Products)
}
The component can be placed in a separate lazy-loaded chunk and fetched when that route is needed.
Lazy Loading Flow
Initially, the user opens:
/
Angular displays:
Home
The Products route has not yet been activated.
Then the user clicks:
<a routerLink="/products">Products</a>
Angular Router sees:
/products
and finds:
{
path: 'products',
loadComponent: ...
}
Then:
User navigates to /products
↓
Angular Router matches "products"
↓
loadComponent() executes
↓
import('./products')
↓
Lazy chunk is requested if not already loaded
↓
Products component becomes available
↓
Products displayed in <router-outlet>
Lazy Loading Multiple Routes
Suppose your application has:
/products
/orders
/admin
You could configure:
export const routes: Routes = [
{
path: 'products',
loadComponent: () =>
import('./products')
.then(m => m.Products)
},
{
path: 'orders',
loadComponent: () =>
import('./orders')
.then(m => m.Orders)
},
{
path: 'admin',
loadComponent: () =>
import('./admin')
.then(m => m.Admin)
}
];
Conceptually:
Initial Application
│
├── /products
│ ↓
│ Load Products
│
├── /orders
│ ↓
│ Load Orders
│
└── /admin
↓
Load Admin
Lazy Loading a Group of Routes
For a larger feature, instead of lazy loading only one component, you can lazy load an entire route configuration using loadChildren.
For example:
products/
├── products.routes.ts
├── product-list.ts
└── product-details.ts
products.routes.ts
import { Routes } from '@angular/router';
import { ProductList } from './product-list';
import { ProductDetails } from './product-details';
export const PRODUCT_ROUTES: Routes = [
{
path: '',
component: ProductList
},
{
path: ':id',
component: ProductDetails
}
];
Then in the main routes:
export const routes: Routes = [
{
path: 'products',
loadChildren: () =>
import('./products/products.routes')
.then(m => m.PRODUCT_ROUTES)
}
];
Now:
/products
↓
Lazy load PRODUCT_ROUTES
↓
ProductList
/products/10
↓
Lazy load PRODUCT_ROUTES
↓
ProductDetails
This is very useful for large feature areas.
loadComponent() vs loadChildren()
loadComponent() |
loadChildren() |
|---|---|
| Lazy loads a component | Lazy loads a route configuration / feature |
| Good for a single page | Good for multiple related routes |
| Common with standalone components | Common for feature route groups |
| Simple route | Larger feature area |
Example
Single page:
{
path: 'about',
loadComponent: () =>
import('./about')
.then(m => m.About)
}
Feature containing several pages:
{
path: 'products',
loadChildren: () =>
import('./products/products.routes')
.then(m => m.PRODUCT_ROUTES)
}
How Was Lazy Loading Done Before Standalone Angular?
In traditional NgModule-based Angular, lazy loading commonly loaded a feature module.
For example:
{
path: 'products',
loadChildren: () =>
import('./products/products.module')
.then(m => m.ProductsModule)
}
The feature module could then import its routing module:
@NgModule({
imports: [
CommonModule,
ProductsRoutingModule
]
})
export class ProductsModule {
}
And ProductsRoutingModule used:
RouterModule.forChild(routes)
So the traditional structure was:
AppRoutingModule
↓
loadChildren()
↓
ProductsModule
↓
ProductsRoutingModule
↓
RouterModule.forChild()
Modern standalone Angular can avoid creating feature NgModules just for this purpose.
Eager Loading vs Lazy Loading
| Eager Loading | Lazy Loading |
|---|---|
| Code is part of the initially reachable/static dependency graph | Code is split so it can be loaded when needed |
| Larger initial bundle/work if many features are eager | Can reduce initial bundle/work |
| Simple for small apps | Very useful for large apps |
component: with static import |
loadComponent: / loadChildren: with dynamic import |
For example:
Eager
import { Products } from './products';
{
path: 'products',
component: Products
}
Lazy
{
path: 'products',
loadComponent: () =>
import('./products')
.then(m => m.Products)
}
Advantages of Lazy Loading
1. Smaller initial bundle
The browser doesn't need to download every feature immediately.
2. Faster initial startup potential
Less initial JavaScript can mean less download, parsing, and execution work.
3. Better for large applications
Features such as:
Admin
Reports
Orders
Settings
may not be needed by every user immediately.
4. Feature-oriented structure
Large sections of an application can be separated into their own route configurations.
Key Points
- Lazy loading means loading route-related code when needed instead of eagerly at application startup.
- Standalone components can be lazy loaded with:
loadComponent
- Example:
{
path: 'products',
loadComponent: () =>
import('./products')
.then(m => m.Products)
}
- Groups of routes can be lazy loaded using:
loadChildren
- Example:
{
path: 'products',
loadChildren: () =>
import('./products/products.routes')
.then(m => m.PRODUCT_ROUTES)
}
- Lazy loading can reduce the application's initial JavaScript payload.
- Dynamic
import()enables code splitting for lazy routes. - Older NgModule-based applications commonly used
loadChildren()to lazy load feature modules.
Interview Questions and Answers
1. What is route lazy loading in Angular?
Route lazy loading is a technique where Angular loads the code for a component or feature only when its route is needed, rather than including all feature code in the initial application load.
2. How do you lazy load a standalone component?
Use loadComponent():
{
path: 'products',
loadComponent: () =>
import('./products')
.then(m => m.Products)
}
3. What is loadChildren()?
loadChildren() lazy loads a set of routes or, in older NgModule-based applications, a feature module.
Modern example:
{
path: 'products',
loadChildren: () =>
import('./products/products.routes')
.then(m => m.PRODUCT_ROUTES)
}
4. What is the difference between loadComponent() and loadChildren()?
loadComponent() lazy loads an individual component, while loadChildren() is used to lazy load a group of routes or an NgModule-based feature.
5. Why do we use lazy loading?
The main reason is to reduce the initial amount of JavaScript that must be loaded and processed, which can improve startup performance in larger applications.
6. How was lazy loading implemented before standalone Angular?
NgModule-based applications commonly lazy loaded feature modules:
{
path: 'products',
loadChildren: () =>
import('./products/products.module')
.then(m => m.ProductsModule)
}
The feature module's routing typically used:
RouterModule.forChild(routes)
Interview-ready answer
Route lazy loading in Angular means loading the code for a route only when that route is needed. In modern standalone Angular,
loadComponent()can lazy load an individual standalone component, whileloadChildren()can lazy load a group of routes. Lazy loading reduces the initial JavaScript payload and is especially useful for large applications with features such as Admin, Reports, and Orders.