Implementing Lazy Loading

Mic B. || Angular Lead Programmer
8 min readJan 18, 2022

--

This article is the third of a series about an in-depth understanding of Angular Modules and it details lazy loading. The first one is about introduction concepts and the second one is about feature modules.

Have you ever experienced a long waiting time on the initial load of your application? Have you looked at the network tab in your developer tools? If so, you probably have seen a lot of action in there… The reason for your trouble is that your entire application is loaded and cached in one go.

Fear not for there is a solution to this nightmare: Lazy loading. It can even be enhanced by adding preloading (which I will be discussing in the next article of this series).

What is lazy loading?

Lazy loading is a technique that allows you to load JavaScript components asynchronously when a specific route is activated. When the application is loaded for the first time, only the required modules are loaded and utilized.

For instance, let’s think about a barbershop website. If lazy loading is implemented and I navigate to the shop page of a website, it will load a “chunk” of TypeScript code (a module) for that specific page. Similarly, if I would visit the appointment section, I would expect another module to be loaded.

With this in mind, let’s learn how to lazy load any module with Angular!

Implementing Lazy Loading

Step 1 — Creating the Angular Project

As the first step to every project, we will be creating a new application with the CLI,

Open your favorite IDE and in your terminal, run the command ng new modules in any directory that you prefer.

You will be prompted the following.

Routing: Yes

Stylesheet: your choice

Open the app component and clear everything out of it. Replace it by <h1>Hello World</h1>.

The project is now set up. Navigate to the project directory: cd modules and let’s launch it.

Run the command ng serve — open on your terminal. This will launch the Angular Application on port 4200. You should get the output shown below.

Cleaned new app.

Step 2: Getting the app heavier

Before implementing Lazy Loading, we need a bit of weight to see how it enhances performance.

To do this, let’s simulate an initial load.

Open to the AppModule (app.module.ts) and add ReactiveFormsModule on the imports array.

Then let’s create some components.

Home — About — Contact — Login — SignUp

Run the command ng g c ComponentName to create each component.

Once it is done, they will each be added to the declaration array in the AppModule. This will cause the components to be loaded during the initial page load.

It should be looking like this:

App Module with the components and ReactiveFormsModule.

Let’s observe the terminal now. The main.js file has increased to 17.24KB and there is a vendor file of 2.7MB, bringing the total size of the app to 2.7MB. It is not much at all, but a hundred components and imports would be something different. Even an initial bundle size of around 10 to 15MB can cause a significant increase in initial load time.

Surely, we can do better, can’t we?

Step 3: Lazy loading integration and module separation.

We will create a module for each section of the application and load the required modules when the user accesses the component.

These components are accessed by routes. This means that we will configure lazy loading in the routing module directly in the routes where the required module should be loaded. In short, the modules will be loaded, giving access to its components, when the route is activated.

All is well, but as of now, we only have one module.

There are several approaches on how we should split modules in our application and picking up the right one depends mainly on the size of the application, its structure, functionalities, and your coding style. For this demonstration, we will split our application into five modules, the root module, the routing module, and three features modules.

We will be leaving the HomeComponent in the AppModule given that it will be initially loaded and should have its content available from the start. The rest of the components will be shared amongst two modules: the AuthModule will hold the SignUp and Login components and the InformationModule will contain the About and Contact components. Additionally, we will create a SharedModule to store the common imports we use for all the components.

Module structure

I hear you thinking: “Nice talk, but where are my modules?”

Step 4 — Creating the Modules

Let’s start with the Auth Module. Run ng g m auth. This creates a sub-directory named auth inside the app directory with the module file in it. Move the SignUp and Login components to this folder. You will prompt you to update imports. Click yes.

Let’s do the same for the Information Module. Run ng g m information and move the AboutUs and ContactUs components in it.

One more time for the Shared Module: ng g m shared. This time, nothing to move in it.

You should then remove the four components and the ReactiveFormsModule from your AppModule to prevent errors from arising. At this point, this is what you should see.

Folder Structure and cleaned AppModule

Step 5: Configuring the feature modules

The shared module is very simple, so we will start with this one. In this module, we will place the other modules or components that are re-used by other modules.

For this application, we only have the ReactiveFormsModule that fits the description. It would be used in Contact Us, Login, Signup, therefore, is common to these components which are separated into two different parts of the app. To do so, we will declare the ReactiveFormsModule in the imports (giving access to this Module to the SharedModule) and exports (allowing other modules to pick up the ReactiveFormsModule from SharedModule) arrays.

Using the same principle, we will configure the Auth and the Information modules:

We will declare the components contained by these modules in the imports array, but this time we will not export them, as they will be used only inside the scope of the module. We will also include the SharedModule in the imports array both times as we want to have access to it.

We will also be defining routes inside each of the modules to access the components, imports the RouterModule in the imports and exports array as you can see below. Note that the RouterModule in the imports array calls a method called forChild and passes in the routes.

When exporting the module on the last line of it, make sure to mention “default” before defining the class. (You could also not add it and replace the default value by the module name when defining your lazy loaded routes in step 7)

Auth Module
Information Module

For a larger application, you could also have a separate routing module to keep your files cleaner and respect the separation of concern, but in this case, it is a tiny application, so we are not too much concerned about it.

Now that the most complex part of implementing Lazy Loading in Angular has been taken care of, let’s enable it! To do this, we will define routes that will load the modules when needed.

Step 7: Defining Routes for Lazy Loading

First, we will define the routes in the routing module. As we just did in the feature modules, we will create those routes inside the array called routes. To those, we will add a call to a function called loadChilren with parameters on what to load when the route is activated.

This code snippet was extracted from Angular Documentation.

loadChildren: () => import(‘./items/items.module’).then(m => m.default)

The last step we need to do is to import a router outlet into our root component (AppComponent).

That’s it, we have successfully implemented the Lazy Loading.

Step 8: Testing

To confirm that we have added lazy loading let’s stop and recompile the application. Here are the results:

Results

It might not seem much to have gone through all this trouble to have only 327kb of lazy-loaded chunks. However, for a larger application, you will find they are usually much bigger than the initial one.

To browse the routes: use the URLs shown below.

http://localhost:4200/home: Home

http://localhost:4200/auth/login: Login

http://localhost:4200/auth/signup : Signup

http://localhost:4200/information/about: About

http://localhost:4200/information/contact : Contact

Take a minute to open the network tab and see how it behaves. You will see the lazy loaded module being added when you load a route. If we would have gone to the trouble of implementing links, you would see them loading only when you click on a route that contains a module that was not priorly loaded.

Bonus Step: Improving the workflow

There is nothing wrong with the way we created our app, but let’s be honest, it is a slow workflow. There is one way we can accomplish the same result much faster.

Consider our application is missing a lazy-loaded shop module. We can create it directly in the terminal with the following command: ng g m shop --module app --routing true --route shop

Lazy-loaded route created for ShopModule
Shop routing module

As we can observe, this created for us a shop folder, with inside a module, a routing module, and a component. We are also getting the component routed in the shop routing module and a lazy loaded route in the app routing module. You can’t do faster than that.

And there we have it! This is how we implement lazy loading successfully in Angular.

As we can recall, it is possible to preload a module as well. This will be the subject of the next article in this series.

This has been a long journey to go through together, but we created a new application capable of lazy loading from scratch! I hope, as I always do, you learned something new while reading this.

Thank you.

--

--

Mic B. || Angular Lead Programmer

Web programmer for two years now. I learned on my own and started out by freelancing before finding my first programming position at APRIL Canada.