Lazy loading with Angular

Robin van Tienhoven
Team Rockstars IT
Published in
6 min readJan 19, 2022

These days web applications can become quite big. A dashboard consisting of 10 components, another page with 2–6 components and of course some services providing data for all the separate components.

Each of those components also consists of a HTML page, a stylesheet and the actual logic in a TypeScript file. All of this code is automatically bundled by Webpack, which makes deploying a lot easier. But it also has a downside: the browser needs to download these files when loading the page and it can only start loading your web application when the entire bundle has been downloaded.

Angular and Webpack already offer some nifty out-of-the-box features that help you minimize the size of your bundles, such as Code minification, Tree shaking and the AOT Compiler (which is automatically activated in dev mode when running Angular 9 or above, to improve the developer experience).

But there are still a few more steps you can take to manually improve the performance of your application. One of these features is offered by the Angular Router, called “Lazy loading”.

Lazy loading Modules

The Angular router manages the flow of your whole application, it knows when it should load a certain page or when a page should be destroyed (or cached). Because it knows which page is about to be loaded, it also knows which Module is necessary to get data to display on the page. As you might have guessed, it can serve as much more than a simple page loader.

Because it knows what Modules (and components) need to be loaded in order to display that page, it can split that Module from the main bundle into a smaller separate bundle. This allows your browser to load a few smaller bundles instead of one big bundle.

You might think that having your browser download more files has a negative impact on your application’s performance, but it is quite the opposite. Let me explain why. When the browser needs to download a single file it can only use a single thread. Because of the way the file is served from the server, it can’t be retrieved chunk by chunk, it has to be downloaded all at once. On top of that, the browser can only run your application when the file has finished downloading.

As you can see a single big file can be quite limiting. But what if you could serve a lot of small files? This is exactly the point of lazy loading. Angular will bundle your Modules in small JavaScript files, based on the page they are used in. When requesting a set of small files, the browser can use multiple threads to download the files in parallel. This way it can download the files faster, which improves the loading time of your application.

In short
When enabling lazy loading, Angular will bundle Modules in small JavaScript packages, which can be loaded separately from the main bundle. This enables the browser to use multithreading, thus downloading your application’s files faster.

Creating our demo application

Now that we know the theory behind lazy loading we can look at an actual use case. For this example we are going to create a simple demo app with a question page and an answer page that shows the correct answer.

First, we need to create a new project:

ng new question-answer-demo —-minimal --routing —-style=scss

Now that we have created our project we can start programming.

Adding lazy loading

Adding lazy loading to our application is fairly straightforward. All we need to do is change the way we define our routes. But before we can add routes we need to have a second page we can redirect to. So let’s create the answer page:

ng generate module answer
ng generate component answer

Now that we’ve created our answer page, we can add it to our routing file. Open the src/app/app-routing.module.ts and replace the routes with the following:

This will load our answer page on startup. The .then(m => m.AnswerModule) will load the actual Module that is connected to the page when the route is triggered. This will, however, load just the Module and not the components that are related to that Module. Because of this we need to add a router path to the Module definition, which tells the router to also load the component when the Module is loaded.

Let’s add this path to the Module, go to src/app/answer/answer.module.ts and add the following code:

Now that we have defined the route we need to register it as a child route with the router, so that the router knows what component to load on load of the module. We can achieve this by calling the RouterModule.forChild method.

And that’s it! If we now fire up our application via ng serve you can see that the answer page has been loaded with the text answer works!.
(Optional) You can replace the template parameter of the app.component.ts decorator with the following to make it clearer:

Viewing the result

So now we have a working application, the answer page loads and everything looks okay. But how can we check whether the lazy loading has an actual effect on the build output? Just run ng build and look at the /dist folder. You will find a separate module file for the answer Module in there:

build output without lazy loading
Build output of with lazy loading

As we compare the two images above we see an extra file in the second image called 414.9be(...) . If we open this file we can see a few references to our Answer component. This means that the Angular router told the Angular compiler that the file can be loaded later than the other files (lazy loaded) and thus can be separated from the main bundle. Now we have an extra file, which means a separate file that can be loaded asynchronous with the main bundle by the browser.

Mission accomplished!

Preloading all the Modules

Lazy loading already improves the performance of the application by splitting up the bundle in multiple smaller bundles. But there is still a catch. The application will still download every resource on app load, even the bundles that are not yet needed. Which still means that we need to wait until everything has finished downloading before we can load the application.

For this the Angular Router has a second trick up its sleeve. We can change this behavior by setting the preloadingStrategy of the Angular Router to PreloadAllModules. This allows the router to delay the download of bundles which aren’t directly required to a period of inactivity after the initial application load. With this, fewer resources have to be loaded upfront, which results in shorter loading times of your application.

Changing the loading strategy is even easier than implementing lazy loading, because it only requires an extra configuration object for the router:

Don’t forget to add the configuration object as the second parameter of the .forRoot method.

That’s it! That is all we need to make it work. If we now run ng serve and look at the network tab in our browser, we can see that the src_app_answer_answer_module_ts.js bundle is loaded after the initial load!

Inspector network tab showing the loading of the answer bundle

Conclusion

As you can see, there are still a few tricks that you can implement to improve the performance of your application. Some are easier than others, but implementing lazy loading definitely belongs to the easy category.

Also with the new Ivy compiler, that automatically ships with Angular since version 9, you can even lazy load individual components for even more control and performance boosts. A nice article covering the lazy loading of components can be found here on Medium.

If you want to see the whole application and not just individual parts: check it out on my github page.

--

--

Robin van Tienhoven
Team Rockstars IT

I am a front-end developer and Special Agent (thought leader) at Team Rockstars-IT, where I work with technologies such as Angular, RxJS and Vue.js