Beginner’s Guide to Lazy Loading in Angular

Luis Ramirez Jr
Luis Ramirez Jr
hero image

Have you ever loaded up your Angular app and something feels off? For some reason it’s loading just a little too slow, even though the code’s solid and the design looks good - so what gives?

Well, chances are that your app is working harder than it needs to. Possibly even loading things users haven’t even asked for yet and making them wait for what they actually want.

Not great right?

Especially when you consider that if someone has to wait more than 3 seconds online, they’ll probably stop what they’re doing and do something else.

The good news is that this is all fixable. In fact, we can use 2 different features that are built into Angular and can seriously improve performance. And in this guide, I’ll show you how to use each of them, explain when to use one over the other, and help you avoid common mistakes along the way.

Sidenote: If you want to learn more and improve your Angular skills, then check out my complete Angular Developer course:

learn angular

This is the only Angular course you need to learn Angular, build enterprise-level applications from scratch, and get hired as an Angular Developer. Completely revamped for 2025!

With that out of the way, let’s get into this guide.

What is lazy loading?

Lazy loading might sound like a fancy technical term, but the concept is actually really simple. It’s just a smarter way to manage when and how your app loads different parts of itself.

So here’s the deal:

When you first start building an app, it’s probably small with just a few features. So you can be lazy (no pun intended) when you design because you can kind of brute force load times.

It’s less stuff to load so you can let it all load at once. We call this eager loading.

However, as your Angular app grows, it’s probably going to get a lot of features, routes, and components. And so now if you try to load all of that upfront, the app gets weighed down.

This means that even if a user only needs a single page, they’re stuck waiting while everything loads behind the scenes.

Not great right?

The solution to this is lazy loading. Rather than loading everything at once, Angular waits until a user actually needs a feature before it loads the code for it.

For example

If someone visits your homepage, they only get the code for the homepage. If they later click into the dashboard, then and only then does Angular go fetch the code for the dashboard.

It’s a bit like only packing what you need for today’s trip, rather than dragging around a full suitcase just in case you might use something later.

It seems simple because it is, but this small shift can make a huge difference in performance. The app loads faster, it feels more responsive, and users don’t get stuck waiting on things they’re not even using yet.

The question of course is how do we do this?

How lazy loading works in Angular

Now that you’ve got the concept, let’s talk about how lazy loading actually works in Angular.

At a high level, Angular gives you two main ways to lazy load parts of your app. You can lazy load entire modules or individual standalone components. Both approaches are built into the Angular Router, and which one you use depends on how your app is structured.

If your app uses feature modules — and most Angular apps still do — you’ll probably use the loadChildren method. This tells Angular, “Don’t load this module until the user navigates to this route.” Super handy when you’ve got different sections like an admin panel, user dashboard, or settings area that don’t need to be available right away.

But if your app is built using standalone components, which became available in Angular 14 and got even better in later versions, you can use the newer loadComponent method. This lets you skip creating a full module just to lazy load a feature. It’s a bit more lightweight and modern, especially for smaller apps or micro frontends.

In both cases, Angular listens to the router. When the user navigates to a route you’ve set up for lazy loading, Angular grabs the code it needs and brings it in on demand.

It’s worth knowing that this isn’t just some fancy trick. Under the hood, Angular is working with something called code splitting. That’s a bundler feature (from tools like Webpack or Vite) that breaks your app into separate chunks. When you configure lazy loading, Angular sets up those chunks so they can be loaded independently — meaning faster load times and better performance for users.

In the next section, we’ll start with the more traditional approach of lazy loading modules using loadChildren. Then we’ll look at the newer way to lazy load standalone components with loadComponent.

So that by the end of both, you’ll know how to do this no matter how your app is structured and when to use each method.

Lazy loading modules with loadChildren

If your Angular app is built using feature modules, this is the most common way to implement lazy loading.

Here’s how it works:

Instead of importing everything up front in your main AppModule, you split your features into separate modules, like AdminModule, UserModule, or DashboardModule. Then, in your routing setup, you tell Angular to load those modules only when the user navigates to their routes.

This is exactly what loadChildren is for.

Let’s walk through it step by step.

1. Create a feature module and component

First, you generate a module and component for your feature.

For example:

ng generate module dashboard --route dashboard --module app

This command actually does most of the work for you. It sets up a DashboardModule, creates a DashboardComponent, and wires everything up for lazy loading in your AppRoutingModule.

But if you’re doing it manually, it looks something like this:

// dashboard-routing.module.ts
const routes: Routes = [
  { path: '', component: DashboardComponent }
];
// app-routing.module.ts
const routes: Routes = [
  {
	path: 'dashboard',
	loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule)
  }
];

Notice how we’re not importing the DashboardModule at the top of the file. Instead, we’re using a dynamic import() to pull it in only when needed.

2. What happens under the hood

When a user visits /dashboard, Angular sees that the route uses loadChildren, so it fetches the dashboard.module.ts chunk from the server and loads it on the fly. If they never visit that route, the module never gets loaded.

That’s lazy loading in action — it reduces the amount of JavaScript downloaded during the initial app load, which means faster startup times and a better user experience.

###*3. A quick reminder on structure

Your lazy-loaded module needs its own routing module (DashboardRoutingModule) and should declare its own components. It should not be imported into AppModule — that would defeat the whole purpose of lazy loading.

Lazy loading standalone components with loadComponent

If you’re building your Angular app using standalone components — which is fully supported as of Angular 14 and beyond — you can skip modules altogether and lazy load components directly. This approach is cleaner, faster to set up, and often makes more sense in smaller apps or modern frontend architectures.

Instead of telling Angular to load an entire module, you just tell it to load a single component when the route is triggered. It’s a more streamlined way to build apps, especially now that Angular doesn’t require everything to live inside a module.

Let’s look at how to do it.

1. Create a standalone component

You can generate a standalone component like this:

ng generate component dashboard --standalone

This creates a DashboardComponent that’s not tied to any module. You’ll see that it includes the standalone: true flag in the component metadata:

@Component({
  standalone: true,
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent { }

2. Configure the route with loadComponent

Now, instead of using loadChildren, you’ll use loadComponent in your routing configuration:

// app-routing.module.ts
const routes: Routes = [
  {
	path: 'dashboard',
	loadComponent: () =>
  	import('./dashboard/dashboard.component').then(m => m.DashboardComponent)
  }
];

Just like with lazy-loaded modules, Angular will wait to load this component until the user navigates to /dashboard.

3. What makes this useful?

The real power of loadComponent is simplicity, because there’s no need to create a module, set up separate routing, or wire up anything beyond the component itself. This means that if your app doesn’t need module-level organization, or if you want to keep things lightweight, then this method can save time and reduce boilerplate.

That said, you can also mix and match. Some apps still use modules for larger sections, and standalone components for smaller, self-contained features.

When to use loadChildren vs loadComponent

Now that you’ve seen both methods, you might be wondering which one should you actually use?

Here’s a simple way to think about it:

Use loadChildren if:

  • Your app is structured around feature modules
  • You want to keep concerns separated at the module level
  • You’re working on a larger app, especially with multiple teams or sections
  • You already have routing set up inside each feature module

This is the most common setup in Angular apps, especially in enterprise-scale projects. Modules are helpful for organizing code, grouping related components, and managing things like shared services.

Use loadComponent if:

  • You’re building your app with standalone components
  • You want a lightweight, modern setup with less boilerplate
  • Your features are simple or self-contained
  • You’re starting a new project and don’t need the structure of full modules

This approach is newer and becoming more popular. It’s faster to scaffold, easier to reason about in smaller projects, and fits well with Angular’s push toward modular simplicity in recent versions.

Can you mix both?

Yes, and in many cases, you should.

You might lazy load entire sections of your app with loadChildren, and then use loadComponent inside those sections for smaller features or routes that don’t need their own module. Angular supports both, and they play well together.

You just need to make sure you use them correctly…

Best practices and common pitfalls

Lazy loading can speed up your Angular app and keep your codebase clean, but it’s also easy to make mistakes - especially when you’re just getting started. So before you run off and start breaking your app into chunks, here are a few things to keep in mind.

Don’t import lazy-loaded modules into AppModule

This is the number one mistake beginners make.

If you import a feature module into AppModule, Angular will load it immediately, even if you’ve set it up with loadChildren.

Why does this matter?

Simply because by doing this, it completely defeats the point of lazy loading, because it’s now part of the initial bundle and will slow down the load speed.

Remember: If you want Angular to lazy load a module, let the router handle it. Don’t import it manually anywhere else.

Keep lazy-loaded modules self-contained

Each lazy-loaded module should manage its own components, services, and routes. Think of it as its own mini app inside your main app. That separation helps with performance and also makes your code easier to manage.

It also means any shared services should live in a shared module or be provided in root, not directly in a lazy-loaded module, unless you want them to have their own instance.

Use lazy loading strategically

Don’t just break up your app into modules for the sake of it. Lazy loading works best when you apply it to routes that aren’t immediately needed.

For example:

  • A public landing page? Probably eager loaded
  • An admin panel? That’s a great candidate for lazy loading
  • Account settings or reports that only power users access? Also perfect for lazy loading

The goal is to keep the initial load small, so your users get a fast, smooth experience from the start.

Watch out for layout shifts

If your lazily loaded routes include heavy components or third-party libraries, there might be a short delay the first time they load. During that time, the user could see a blank space or layout jump and click on the wrong thing.

To avoid that, you can:

  • Show a loading spinner or skeleton UI while the content loads
  • Use route guards or canLoad to delay navigation until the chunk is ready

It doesn’t need to be fancy. Even a simple “Loading…” message goes a long way to providing a better user experience and not having a CLS issue.

Automatically converting routes to lazy loading

Chances are, you may be working in an application where all the routes are eager loaded. If that’s the case, you may have the idea of lazy loading some of the routes, but you may be hesitant because there are simply too many routes. It’s not fun going through dozens of files updating routes.

Luckily, Angular offers a single command to allow you to convert existing routes into lazy loaded routes.

ng generate @angular/core:route-lazy-loading

This command is going to affect all routes, so be careful before running it. If you only want to convert a few routes, you can specify them with the following command:

ng generate @angular/core:route-lazy-loading --path src/app/sub-component

When providing a path, it must be relative to your application’s root.

Start lazy loading your own Angular projects!

Lazy loading is one of the easiest ways to make your Angular apps faster and more efficient, and you’ve already seen how simple it can be.

Now it’s your turn to put it into practice.

Look at your current project. Find one feature, one route, or one component that doesn’t need to be loaded upfront. Try breaking it out into a lazy-loaded module or standalone component.

Start small. Test it out. See the difference in load time and responsiveness. Once you’ve done it once, it’ll start to click and you’ll find more opportunities to optimize as you go.

P.S.

Remember, if you struggle with any of the concepts in this guide, or want to take a deep dive into Angular, check out my complete Angular Developer course:

learn angular

This is the only Angular course you need to learn Angular, build enterprise-level applications from scratch, and get hired as an Angular Developer.

Want a sneak peak?

I went ahead and uploaded the first 4 hours of the course below for free:


Plus, once you join, you'll have the opportunity to ask questions in our private Discord community from me, other students and working professionals.


Not only that, but as a member - you’ll also get access to every course in the Zero To Mastery library!

Check out more Angular content!

Check out some of my other Angular articles, guides and cheatsheets!

More from Zero To Mastery

Top 10 Angular Projects For Beginners And Beyond preview
Popular
Top 10 Angular Projects For Beginners And Beyond
9 min read

Are you looking to skill up in Angular? We give you 10 Angular practice projects to make that happen plus our Top 3 you can't miss for you to up your game!

Top 25 Essential Angular Interview Questions + Answers preview
Top 25 Essential Angular Interview Questions + Answers
26 min read

❌ Don't try to learn 100+ questions! These are the CORE 25 angular interview questions that if you can understand and answer - you'll ace anything they ask.

Beginner’s Guide to Angular Signals preview
Beginner’s Guide to Angular Signals
22 min read

Struggling with Angular state? This guide shows how Signals fix it, with faster change detection, less code, and a whole lot less RxJS pain. (With code examples).