By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules, whether or not they are immediately necessary. For large apps with lots of routes, consider lazy loading — a design pattern that loads NgModules as needed. Lazy loading helps keep initial bundle sizes smaller, which in turn helps decrease load times.
To lazy load a feature module you can simply define a route for it and that’s it!
Here is an example to remind you how it’s done.
const routes: Routes = [
loadChildren: () => import('./map/map.module').then(m => m.MapModule)
Now Angular CLI will separately bundle
map module and it will only be loaded when you access the
What about modules used in the map that do not have a separate route?
Like modals or any piece of UI component that is not visible in view initially and will appear based on different workflows.
By default, all these modules will be loaded with the map module bundle, even though if the user never uses these features.
Lazy load feature modules in an already lazy-loaded feature module
In my application, I have a map view which is a feature module with the route
/mapthat hosts a bunch of other features.
- Search modals that will only be visible when the user is searching on the map.
- Layer selectors to find a layer to add to map which is collapsible. So it will be only visible when expanded.
- Layer details which are only visible when a user clicks on a layer in the side panel to see its details.
- Also, some more UI pieces that will be visible when the user selects a specific menu. Like Draw controls, Print form, Set Region control and more.
- Plus each one of these UIs utilises services and other dependencies under the hood which can quickly add up to map module bundle size.
I want to load these pieces of UI only if the user needs them.
Obviously, I can lazy load the other feature modules like map module but the caveat is that accessing routes that define other feature module means leaving and unloading the map which is not going to work for my use-case.
For example, I need to stay on the map and show the modal.
So I need to lazy load feature modules in an already lazy-loaded
This will help me to reduce the initial bundle size of the
/map module to make loading of the map faster.
So the question is:
How to lazy load those feature modules without leaving the
Angular Auxiliary Routes to the rescue
Angular Auxiliary Routes let you lazy load parts of the view that are not visible and bundle those separately.
The following video shows how invest.agriculture.vic.gov.au uses this capability. Here I am starting in the context of
/map route and all the JS required for the visible part of the view is loaded with the map module.
Pay attention to network requests when I open the search results modal. It’s only then that Angular loads the JS files required for the SearchModule and my modal.
invest.agriculture.vic.gov.au is too complicated for the learning purpose so I made an Example application that replicates the lazy-loaded modal and map.
This application is available on Github. So you can download and follow the same steps mentioned in the rest of my article.
This project was generated with Angular CLI version 10.1.7. Run ng serve for a dev server. Navigate to…
Here is a demo of the final result.
Let’s see what is happening with the URL here
This is the syntax for the URL
http://localhost:4200is the base path. I am using HashLocationStrategy which is why I have
/#/after base URL.
/mapis the primary path for the map view.
- The remaining part in the parenthesis is my auxiliary route (route within the
Let’s breakdown the auxiliary route
)close parenthesis indicates the beginning and end of my auxiliary route.
- Followed by
map-outletwhich is my outlet name. This is the name I selected for my secondary outlet. I will explain this further shortly.
- Followed by
:that separates the outlet name from the actual path.
In this case, the path is
modalfor modal. I am using short names here to shorten my URL length but you can use any name.
How to build the auxiliary routes?
First, I need a secondary router outlet (named router outlet).
In Angular, you can have one default(primary) router outlets and as many named(secondary) router outlets as you need.
RouterOutletis a directive from the router library that is used like a component. It acts as a placeholder that marks the spot in the template where the router should display the components for that outlet.
Given the configuration above, when the browser URL for this application becomes
/map, the router matches that URL to the route path
/map and displays the
MapComponent as a sibling element to the
RouterOutlet that you've placed in the host component's template.
The primary router outlet will render
map-outlet (named router outlet) will render the map component.
In my case, both router outlets are in the
All I need now is to specify the outlet name in the map module router when I define the base route and place a 3rd
<router-outlet> where I want to load map modules’ child routes. The 3rd
<router-outlet> is placed in
Here is the top-level application router config defining the
/map route which lazy loads the map module.
Next, I need to define the child routes for the
map module and use my named router outlet (
This is the code for the map module router that has a child route which use the
map-outlet (named router outlet) to load ModalWrapperModule.
That’s all you need to define a route within a route and Angular will take care of bundling the lazy-loaded feature modules separately and also lazy loading the feature modules as you navigate to their respective route.
Here is the code structure for the map module.
Now you can easily use the Angular router to navigate between to Modal route. Take a look at the
As you can see I am passing multiple commands to navigate method.
Basically telling the router to navigate to
/map is the map module path,
map-outlet is my named outlet and
modal is the path for the modal module.
And the last piece of the puzzle to make the router work. Place a
<router-outlet></router-outlet> in the map view HTML.
By putting an outlet in the
map.component.html I am telling the Angular where to render the content of the named router outlet (
How does the code look like for the modal module?
The architecture for the module is a bit more complicated than the other feature modules in my application since with Angular material dialog I need to pass a component to the
Here is how the code structure looks like.
modal-wrapper.component.ts is a wrapper component that will open the modal in the
OnInit when the module is loaded by modal route (
Plus it will clean up the route to remove the auxiliary route when modal is closed. I am using
afterClosed method of Angular Material Dialog to detect when the user closes the modal and change the route to the
/map and remove the auxiliary route.
OnDestroy will take care of the scenario that user clicks browser navigation back button.
This way I can navigate between modal and map as many time as I want to show/hide the modal.
Here is the code for each file in the modal module.
I have simplified the code here for this demonstration but you can imagine that a feature module can be way more complicated and use more services or other third-party dependencies.
So being able to lazy-load modules will be a great boost to the application performance.
- As a result of this architecture, you modal is route enabled which means you can refresh the page and Angular will open the modal automatically.
This is great for bookmarking and sharing of URLs.
In this case, I can share the link with someone and they can see the search result for the exact lat/long I used for my search.
- By opening each lazy-loaded route that is defined similarly to the search module on the map I navigate away from the search module route and search modal will get destroyed.
This can be good or bad based on the use-case.
In my case, this is exactly what I needed.
For example, I want to close the search modal when I open the layer selector and vice versa.
This will naturally happen as I move away from the previous route.
Without this architecture, you will need to keep some flags locally in map component or on the app state to make sure you close the layer selector component before opening the search modal which can get really messy in a big application.
Of course, you can use a state management system like NgRX and Redux to help you with managing the state.
- You can skip the wrapper component (
modal-wrapper.component.ts) in your implementation if you have a simpler UI to show/hide.
The wrapper component is really useful to support the usage of Angular Material Dialog.
- You can prefetch the lazy-loaded modules using the
preloadingStrategy: PreloadAllModulesor use a custom
preloadingStrategyas described in the following article.
Also if your requirement is just to lazy-load a specific component like a modal and not the whole feature module, you might find the following article useful.
Lazy Load Modal Components in Angular
Using modals is one of the most popular UX concepts, which almost every application uses. In most cases, we’ll open the…
Thanks for reading my article.
I hope you find it useful.