Angular is a platform for building applications in TypeScript, HTML and CSS.

The basic building blocks of an Angular application are NgModules. An Angular app is typically comprised of a root module (that is responsible for bootstrapping) and one or more feature modules.

By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules. However, Angular also provides support for lazy-loading NgModules. Lazy loading helps keep initial bundle sizes smaller, which in turn helps decrease load times.

Angular Libraries

Many applications need to solve similar problems so software engineer's like to craft generic solutions (for a particular domain) that can be re-used by other applications.

Angular Material (for example) is an Angular library that provides a comprehensive suite of UI components suitable for desktop, tablet and mobile devices.

Getting Started

I used the Angular CLI to generate the scafolding for each of my project's libraries, for example:

ng generate library sales

The CLI will create a new folder (/sales) in your workspace's /projects directory:

├── /serendipity
    └── /e2e
    └── /node_modules
    └── /projects
        └── /sales
    └── /src
        └── /app
            └── /core
        └── /assets
        └── /environments
        └── /themes
        ├── index.html
    ├── angular.json
    ├── package.json
    ├── tsconfig.json

Using your library

You don't have to publish your library in order to use it in your own app, but you do have to build it:

ng build sales

And, import from the library (in your core module) by name:

import { SalesModule } from 'sales';

Lazy loading your library

First we need to create a wrapper module (sales-lib-wrapper.module.ts):

import { NgModule } from '@angular/core';

import { SalesModule } from 'sales';

  imports: [ SalesModule ]
export class SalesLibWrapperModule {}

Then we need to update our App routing module as follows:


const routes: Routes = [

    path: 'sales',
    loadChildren: './lazy-loading/sales-lib-wrapper.module#SalesLibWrapperModule'


  imports: [
    RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload'})
  exports: [
export class AppRoutingModule {}

Now, that we are lazy loading the Sales module (in the App routing module), every route in the Sales library routing module is a child route:


const routes: Routes = [

    // path: 'sales/contacts',
    path: 'contacts',
    component: ContactsComponent,
    canActivate: [AuthGuard],
    runGuardsAndResolvers: 'always'

  imports: [ RouterModule.forChild(routes) ],
  exports: [ RouterModule ]
export class LazyLibRoutingModule {}

And we no longer need to (statically) import the SalesModule (in the core module):

// import { SalesModule } from 'sales';

To help you visualise bundle sizes you can use the Webpack Bundle Analyzer.

For example ("target": "es5"):

ng build --prod --stats-json && ./node_modules/webpack-bundle-analyzer/lib/bin/analyzer.js ./dist/serendipity/stats.json

For example ("target": "es2015"):

ng build --prod --stats-json && ./node_modules/webpack-bundle-analyzer/lib/bin/analyzer.js ./dist/serendipity/stats-es2015.json


If you are lazy loading a library that relies on another libraries entryComponents then you need to ensure that its (entry component-related) services are providedIn the library (module), for example:

import { Injectable } from '@angular/core';
import { MatDialog, MatDialogRef, MatDialogConfig } from '@angular/material/dialog';
import { ComponentType } from '@angular/cdk/portal';

import { AlertDialogComponent } from '../../components/dialogs/alert-dialog/alert-dialog.component';
import { ConfirmDialogComponent } from '../../components/dialogs/confirm-dialog/confirm-dialog.component';

import { SerendipityComponentsModule } from '../../serendipity-components.module';


  // providedIn: 'root'
  providedIn: SerendipityComponentsModule
export class DialogService {


Source Code: