What is Code Splitting in Angular?
1. What is Code Splitting?
Code splitting is a technique that divides an Angular application's JavaScript code into smaller bundles/chunks instead of delivering all application code as one large initial bundle.
The browser downloads the initial code first and can download other chunks when they are needed.
Code Splitting = Break a large JavaScript bundle into smaller chunks.
For example, suppose an application contains:
Home
Products
Orders
Admin
Reports
Instead of producing one conceptual large bundle:
main.js
├── Home
├── Products
├── Orders
├── Admin
└── Reports
code splitting can produce separate chunks:
main.js
products-[hash].js
orders-[hash].js
admin-[hash].js
reports-[hash].js
The exact generated filenames and chunk boundaries are determined by Angular's build tooling.
2. Code Splitting and Lazy Loading
These two concepts are closely related, but they are not exactly the same thing.
Code Splitting
↓
Break code into separate chunks
Lazy Loading
↓
Load a chunk only when it is needed
A useful way to remember:
Code splitting creates the pieces; lazy loading determines when deferred pieces are loaded.
For example:
{
path: 'products',
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
}
The dynamic:
import('./products/products')
creates a lazy boundary that allows the build system to place that code into a separate chunk.
Then loadComponent() tells Angular Router when to load it.
3. Full Simple Sample Program
Suppose we have three pages:
Home
Products
Admin
We want:
- Home → eagerly loaded
- Products → lazy-loaded
- Admin → lazy-loaded
Project Structure
src/app/
│
├── app.ts
├── app.html
├── app.routes.ts
│
├── home/
│ └── home.ts
│
├── products/
│ └── products.ts
│
└── admin/
└── admin.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)
},
{
path: 'admin',
loadComponent: () =>
import('./admin/admin')
.then(m => m.Admin)
}
];
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> |
<a routerLink="/admin">Admin</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 {
}
admin/admin.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-admin',
standalone: true,
template: `
<h2>Admin</h2>
<p>Welcome to Admin.</p>
`
})
export class Admin {
}
4. How Does Code Splitting Happen?
Look at the Products route:
{
path: 'products',
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
}
The important part is:
import('./products/products')
This is a dynamic import.
Compare it with a normal static import.
Static import
import { Products } from './products/products';
Then:
{
path: 'products',
component: Products
}
The Products code is part of the eagerly reachable dependency graph.
Dynamic import
import('./products/products')
This establishes a separate asynchronous loading boundary.
Conceptually, the build output might look like:
dist/
│
├── main-[hash].js
├── products-[hash].js
└── admin-[hash].js
Again, the actual filenames and chunk structure can vary.
5. Application Flow
When the application starts:
Browser
↓
Download initial application bundles
↓
Home displayed
The Products and Admin route code can remain deferred.
When the user clicks:
Products
Angular does:
/products
↓
Angular Router
↓
loadComponent()
↓
dynamic import()
↓
Products chunk requested
↓
Products component created
↓
Products displayed
Similarly:
/admin
↓
Admin chunk loaded
↓
Admin displayed
6. Without Code Splitting vs With Code Splitting
Without route-level code splitting
Conceptually:
Application
↓
Large initial bundle
┌──────────────────────┐
│ Home │
│ Products │
│ Orders │
│ Reports │
│ Admin │
└──────────────────────┘
The user may never visit Admin, but Admin code may still be included in the eagerly loaded dependency graph.
With Code Splitting
Initial
↓
main.js
↓
Home
User → Products
↓
products chunk
User → Admin
↓
admin chunk
Now the initial workload can be smaller.
7. loadComponent() and Code Splitting
For a single standalone component:
{
path: 'products',
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
}
Think:
loadComponent()
↓
Dynamic import
↓
Separate lazy chunk
↓
Downloaded when route is needed
8. loadChildren() and Code Splitting
Code splitting can also happen at the feature route level.
Suppose Admin has:
Admin
├── Dashboard
├── Users
└── Reports
Main route:
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
Then:
Main Application
|
+--- main chunk
|
+--- Admin lazy boundary
|
+--- Dashboard
+--- Users
+--- Reports
And the individual Admin pages can themselves use loadComponent():
export const ADMIN_ROUTES: Routes = [
{
path: 'dashboard',
loadComponent: () =>
import('./dashboard')
.then(m => m.Dashboard)
},
{
path: 'users',
loadComponent: () =>
import('./users')
.then(m => m.Users)
}
];
This allows additional splitting inside the feature.
9. Code Splitting vs Lazy Loading
This is an important interview distinction.
| Code Splitting | Lazy Loading |
|---|---|
| Divides code into chunks | Loads deferred code when required |
| Build/bundling concept | Runtime loading strategy |
| Creates separate bundles/chunks | Controls when those chunks are requested |
Dynamic import() enables a split point |
Router APIs use dynamic imports for lazy routes |
| Improves modular delivery | Reduces initial loading work |
Easy way to remember
Imagine you have a 500-page book.
Code splitting:
500-page book
↓
Split into
↓
Book 1
Book 2
Book 3
Book 4
Book 5
Lazy loading:
Need Book 1
↓
Get Book 1
Later need Book 4
↓
Get Book 4
So:
Splitting = divide. Lazy loading = load on demand.
10. Advantages of Code Splitting
Smaller initial JavaScript workload
Instead of downloading feature code immediately, the initial bundle can contain only what is needed for startup.
Faster initial loading
Less JavaScript may need to be:
Downloaded
↓
Parsed
↓
Compiled
↓
Executed
Better for large applications
Especially useful for applications with separate areas such as:
Public
Admin
Reports
Analytics
Settings
Management
Users don't immediately download unused features
For example, a normal user may never access:
/admin
The Admin feature can remain deferred.
11. Disadvantages
Code splitting also has some trade-offs.
Additional network requests
When the user first visits a lazy feature, the browser may need to request another JavaScript chunk.
Click Admin
↓
Download Admin chunk
↓
Display Admin
This can introduce a small delay on first navigation.
Too many tiny chunks
Excessive splitting can create unnecessary loading overhead.
Therefore, features should be split at sensible boundaries.
12. Code Splitting in Angular + ASP.NET Core
Code splitting concerns your Angular frontend code.
It is different from loading application data from ASP.NET Core.
For example:
Browser
↓
Angular initial bundle
↓
Home
User clicks Products
↓
Products Angular chunk loaded
↓
Products component starts
↓
HttpClient
↓
ASP.NET Core Web API
↓
SQL Server
↓
JSON data returned
There are two separate operations:
Angular Code
↓
Code Splitting + Lazy Loading
Product Data
↓
HTTP API Request
Key Points
-
Code splitting divides application JavaScript into smaller chunks.
-
It helps reduce the amount of JavaScript required during initial startup.
-
Dynamic
import()creates an asynchronous boundary that bundlers can split.
import('./products/products')
- Angular routing commonly uses code splitting through:
loadComponent()
and:
loadChildren()
-
loadComponent()can split and lazy-load an individual standalone component. -
loadChildren()can split a feature's routing configuration. -
Code splitting and lazy loading are related but different:
Code Splitting
↓
Creates separate chunks
Lazy Loading
↓
Loads deferred chunks when needed
Interview Questions and Answers
1. What is code splitting in Angular?
Code splitting is the process of dividing an Angular application's JavaScript into smaller chunks so that all feature code doesn't need to be included in the initial load.
2. Why do we need code splitting?
It can reduce the initial JavaScript bundle and improve startup performance by deferring code for features that aren't immediately required.
3. How is code splitting implemented in Angular?
A common approach is using dynamic import() through Angular Router APIs such as:
loadComponent()
and:
loadChildren()
4. What is the difference between code splitting and lazy loading?
Code splitting divides the application into separate chunks.
Lazy loading loads deferred chunks only when they're required.
Code Splitting → Divide
Lazy Loading → Load on demand
5. What role does import() play?
Dynamic import() creates an asynchronous module-loading boundary that allows the build system to generate separate chunks.
import('./products/products')
6. Does loadComponent() support code splitting?
Yes. For example:
loadComponent: () =>
import('./products/products')
.then(m => m.Products)
The Products component can be placed behind a separate lazy-loading boundary and loaded when the route is accessed.
7. Does loadChildren() support code splitting?
Yes. It can lazy-load an entire feature route configuration:
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
Interview Quick Revision
CODE SPLITTING
│
│
Break application code
into smaller chunks
│
┌─────────┴─────────┐
│ │
loadComponent() loadChildren()
│ │
Single component Feature routes
│ │
└─────────┬─────────┘
│
Lazy Loading
│
Load when required
One-line interview answer:
Code splitting divides an Angular application into smaller JavaScript chunks, while lazy loading allows those deferred chunks to be loaded only when they are needed.