Angular Lazy Loading: Standalone Components, loadChildren() and loadComponent()
In modern standalone Angular, lazy loading is simpler because we do not need to create an NgModule just to lazy-load a component.
The two important router APIs are:
loadComponent() → Lazy-load ONE standalone component
loadChildren() → Lazy-load a GROUP of routes
1. How Is Lazy Loading Implemented with Standalone Components?
What is it?
With standalone components, Angular can lazy-load a component directly through the router using:
loadComponent()
For example:
{
path: 'products',
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
}
When the application starts, the Products component isn't part of that route's eager import.
When the user navigates to:
/products
Angular dynamically loads the component.
Full Simple Sample Program
Project Structure
src/app/
│
├── app.ts
├── app.html
├── app.routes.ts
│
├── home/
│ └── home.ts
│
└── products/
└── products.ts
app.routes.ts
import { Routes } from '@angular/router';
import { Home } from './home/home';
export const routes: Routes = [
{
path: '',
component: Home
},
{
path: 'products',
loadComponent: () =>
import('./products/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],
templateUrl: './app.html'
})
export class App {
}
app.html
<h1>My Application</h1>
<nav>
<a routerLink="/">Home</a> |
<a routerLink="/products">Products</a>
</nav>
<hr>
<router-outlet></router-outlet>
home/home.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
standalone: true,
template: `
<h2>Home</h2>
<p>Welcome to Home.</p>
`
})
export class Home {
}
products/products.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-products',
standalone: true,
template: `
<h2>Products</h2>
<ul>
<li>Laptop</li>
<li>Keyboard</li>
<li>Mouse</li>
</ul>
`
})
export class Products {
}
How Does It Work?
When the application starts:
Application starts
↓
Home loaded
↓
Products not loaded yet
When the user clicks:
Products
Angular navigates to:
/products
The router finds:
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
Then:
User → /products
↓
Angular Router
↓
loadComponent()
↓
Dynamic import()
↓
Products component loaded
↓
Products displayed
The important point is that the component isn't statically imported into app.routes.ts.
We don't do:
import { Products } from './products/products';
for this lazy route.
2. What is loadComponent()?
What is it?
loadComponent() is an Angular Router route property used to lazy-load a single standalone component.
Example:
{
path: 'products',
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
}
Think:
loadComponent()= load one component when its route is needed.
loadComponent() Flow
/products
↓
loadComponent()
↓
products.ts downloaded/loaded
↓
Products
↓
<router-outlet>
Eager Component vs Lazy Component
Eager
import { Products } from './products/products';
export const routes: Routes = [
{
path: 'products',
component: Products
}
];
Because Products is statically imported:
Application
↓
Products reachable eagerly
Lazy
export const routes: Routes = [
{
path: 'products',
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
}
];
Now:
Application starts
↓
Products code deferred
User → /products
↓
Products code loaded
3. What is loadChildren()?
What is it?
loadChildren() is an Angular Router property used to lazy-load child routes / a group of routes.
In modern standalone Angular, it is very useful when a feature contains multiple pages.
For example, suppose Admin contains:
Admin
│
├── Dashboard
├── Users
└── Reports
Instead of putting every Admin route in the main app.routes.ts, we can create:
admin.routes.ts
and lazy-load the entire route collection.
Full loadChildren() Example
Project Structure
src/app/
│
├── app.ts
├── app.routes.ts
│
└── admin/
│
├── admin.routes.ts
├── dashboard.ts
├── users.ts
└── reports.ts
app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
];
admin/admin.routes.ts
import { Routes } from '@angular/router';
export const ADMIN_ROUTES: Routes = [
{
path: 'dashboard',
loadComponent: () =>
import('./dashboard')
.then(m => m.Dashboard)
},
{
path: 'users',
loadComponent: () =>
import('./users')
.then(m => m.Users)
},
{
path: 'reports',
loadComponent: () =>
import('./reports')
.then(m => m.Reports)
}
];
admin/dashboard.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-dashboard',
standalone: true,
template: `
<h2>Admin Dashboard</h2>
<p>Welcome to Admin Dashboard.</p>
`
})
export class Dashboard {
}
admin/users.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-users',
standalone: true,
template: `
<h2>Users</h2>
<p>User management page.</p>
`
})
export class Users {
}
admin/reports.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-reports',
standalone: true,
template: `
<h2>Reports</h2>
<p>Admin reports page.</p>
`
})
export class Reports {
}
How loadChildren() Works
Suppose the user enters:
/admin/users
Angular first sees:
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
It lazy-loads:
admin.routes.ts
Then Angular looks inside:
ADMIN_ROUTES
and finds:
{
path: 'users',
loadComponent: () =>
import('./users')
.then(m => m.Users)
}
Then it loads the Users component.
So:
/admin/users
↓
app.routes.ts
↓
path: 'admin'
↓
loadChildren()
↓
admin.routes.ts
↓
path: 'users'
↓
loadComponent()
↓
Users
This shows how loadChildren() and loadComponent() can work together.
loadChildren() Does NOT Mean "Load Child Components"
This is an important interview point.
The name can be confusing.
loadChildren() doesn't simply mean:
Load child components.
It means:
Lazy-load child route configuration / a feature routing boundary.
For example:
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
The returned value is:
Routes
Traditional loadChildren() vs Modern loadChildren()
This is particularly important because the meaning has evolved with standalone Angular.
Traditional NgModule Angular
loadChildren() commonly loaded an NgModule.
{
path: 'products',
loadChildren: () =>
import('./products/products.module')
.then(m => m.ProductsModule)
}
Flow:
loadChildren()
↓
ProductsModule
↓
ProductsRoutingModule
↓
ProductsComponent
Modern Standalone Angular
loadChildren() can lazy-load a route collection.
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
Flow:
loadChildren()
↓
ADMIN_ROUTES
↓
Dashboard
Users
Reports
Therefore:
Before standalone
loadChildren()
↓
Feature NgModule
Modern standalone
loadChildren()
↓
Routes / feature route configuration
loadComponent() vs loadChildren()
This is the main distinction to remember.
loadComponent() |
loadChildren() |
|---|---|
| Loads one component | Loads child routes / route collection |
| Best for individual page | Best for feature area |
| Common with standalone components | Works with standalone routes and traditional NgModules |
| Returns a component type | Can return routes or a lazy-loaded module |
| Example: Products page | Example: Admin feature |
Think of it like:
ONE PAGE
↓
loadComponent()
MULTIPLE RELATED ROUTES
↓
loadChildren()
Practical Example
Suppose your application has:
Home
Articles
Contact
Admin
├── Dashboard
├── Articles
├── Categories
└── Tags
A sensible routing structure could be:
export const routes: Routes = [
{
path: '',
loadComponent: () =>
import('./home/home')
.then(m => m.Home)
},
{
path: 'articles',
loadComponent: () =>
import('./articles/articles')
.then(m => m.Articles)
},
{
path: 'contact',
loadComponent: () =>
import('./contact/contact')
.then(m => m.Contact)
},
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
];
Here:
Home
→ loadComponent()
Articles
→ loadComponent()
Contact
→ loadComponent()
Admin feature
→ loadChildren()
↓
Dashboard
Articles
Categories
Tags
This is a useful pattern for a standalone Angular frontend with multiple feature areas.
Key Points
-
Standalone Angular doesn't require an NgModule just to lazy-load a component.
-
Use:
loadComponent()
to lazy-load one standalone component.
- Example:
{
path: 'products',
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
}
- Use:
loadChildren()
to lazy-load a feature's child routes.
- Example:
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
-
loadChildren()is not obsolete in standalone Angular. -
Traditional Angular commonly used
loadChildren()for:
Feature NgModule
- Modern standalone Angular commonly uses it for:
Feature Routes
- A common modern design is:
Individual Page
↓
loadComponent()
Feature with many routes
↓
loadChildren()
Interview Questions and Answers
1. How is lazy loading implemented with standalone components?
Standalone components can be lazy-loaded directly using the Angular Router's loadComponent() property.
{
path: 'products',
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
}
2. What is loadComponent()?
loadComponent() lazy-loads a single standalone component when its route is accessed.
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
3. What is loadChildren()?
loadChildren() lazy-loads child routing configuration or, in traditional Angular applications, a feature NgModule.
Modern example:
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
4. What is the difference between loadComponent() and loadChildren()?
loadComponent() is generally used for a single standalone component.
loadChildren() is generally used for a feature containing multiple child routes.
5. Is loadChildren() only for NgModules?
No.
Traditionally:
loadChildren()
↓
NgModule
Modern standalone Angular:
loadChildren()
↓
Routes
6. Can loadChildren() and loadComponent() be used together?
Yes. This is a common standalone routing pattern.
App Routes
↓
loadChildren()
↓
Admin Routes
↓
loadComponent()
↓
Users
For example:
// app.routes.ts
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
and inside Admin:
// admin.routes.ts
{
path: 'users',
loadComponent: () =>
import('./users')
.then(m => m.Users)
}
Quick interview memory
loadComponent()
=
Lazy-load ONE standalone component
loadChildren()
=
Lazy-load a GROUP of routes
|
+-- Dashboard
+-- Users
+-- Reports
Traditional Angular: loadChildren() → Feature NgModule
Modern standalone Angular: loadChildren() → Feature routes, while loadComponent() → individual standalone component.