Angular library lazy loading — part 2

Alain Boudard
5 min readMar 18, 2024

--

In a previous article, we talked about how Angular does bundle and load chuncks of a library, depending on how we organise the code inside and how we expose it outside.

This article should be shorter, and we will simply take a look at the latest (at the time this article is writen) versions of Angular compiler, meaning esbuild and application.

Starting with a multiple feature library

Let’s take the existing application that was created with Angular 16 and move up to v17 :

First, let’s change the builder of our Angular application, you could either use esbuild or application, it won’t make much of a difference, since we have a very simple application. The dist folder is a little bit different with application builder though, it’s useful to notice when running our application on static generated sources.

"builder": "@angular-devkit/build-angular:browser-esbuild",

With the same objective as before, let’s create the features in the core library and see how our bundle works. We start with standalone features and therefore no NgModule, so, our library will look like this :

Angular library with routed features

The routing in our application will look like this :

Angular routes declaration with library features

This configuration is very similar to the previous one, minus the modules, and the compiler does reflect that, with the lazy chunk files like this :

Angular build of lazy chunk files

So, obviously, no difference at all, except a minor improvement in bundle size, compared to the simple browser builder. The whole code of the library is still bundled together, even if we declare lazy loaded features.

Secondary entrypoints

Now, let’s split the code like we did before, using the secondary entrypoints of the library. If you don’t know how to do it, check the previous article. In the end of refactoring, we should have a clean bundle separation like this :

Differential build of Angular library

Our 2 paths for the features routing look now like that :

Angular routing to library secondary entrypoint

Access secondary entrypoint code

Of course we want to check the code interactions that we already know can alter the bundle. We do exactly the same operation as before : add a bills service, expose it and consume it in the main app.component code. And then display the number of pending bills right in the main page header.

Using Angular library service in application

We notice now that the chunk still exists, as soon as we access the route, we can see that it’s loaded :

All seems to be okay, even with code adherence. But if we take a closer look, we see that something is not right with the size of the bundle :

Angular secondary entrypoint with code adherence

Obviously the bundle is much smaller than the original, what’s up with this one ? Let’s see the content of the bills chunk :

Lazy loaded Angular secondary entrypoint

It’s just a shell, asking for some already loaded chunk js file ! This file is only present when the adherence is present, and it’s pre-loaded as initial chunk file :

And the content of this chunk is probably what we would expect from a lazy loaded feature :

Angular 17 pre loaded secondary entrypoint feature

So, basically, the new builder is able to separate the code from the main bundle, unlike the basic browser older builder. But it still has to load this code before it’s even needed.

The same operation as before will result in the expected outcome : move the service in the main entrypoint of the library, and use it where ever we want : either from the application or a secondary entrypoint.

Use code from Angular main entrypoint in secondary entrypoint

Now everything is back to optimal situation with the chunks :

Conclusion

In a nutshell, the advice from the previous article remains, if we want routed lazy loaded features in our Angular library, we need to separate the code that is eagerly loaded from the code that lazily loaded. Any adherence will bring back the code in the initial bundles.

Reference

--

--