In most web apps, images form a huge chunk of the size of the content. If you had, let’s say over 50 images with each having 100Kbs (after compression and resizing) in size, the total of that would be 100 x 50 which is 5MBs in total size. While storage wise, that is not a lot, on a standard 3G internet connection, it would load slowly hence negatively affect the User Experience and the performance of your web app.

This is because, while images won’t affect initial loading and rendering of the site. All 50 images will be competing for the limited bandwidth to be loaded at once, hence loading slowly. This can be rectified by lazy loading images that are not in the view.

If out of the 50 images only 3 images are in view initially, then you only need 300KBs (3 x 100 KB s) initially which is less than 10% of what you required initially. Then as each image comes in to view, it is loaded then and only then. If you combine this with a service worker, to cache the already loaded images, the performance gains could be huge.

Getting Started

For this demo, we will use ng-defer-load library for lazy loading images. This library can also be used to lazy load any element in your angular application. But for now we will just use it with images. We will start by installing and setting it up in our application: NPM:

npm i @trademe/ng-defer-load

// or with yarn
yarn add @trademe/ng-defer-load

Then, import it into your app.module.ts.

@NgModule({
  // ...
  imports: [
    BrowserModule,
    FlexLayoutModule,
    BrowserAnimationsModule,
    DeferLoadModule
  ],
  providers: [],
  // ...
})

Lazy Loading Images

First, we need images to lazy load. We will use images from Placeholder, 100 of them to lazy load them. We are going have a placeholder caption of the image of the image number. Then, we will generate the image collection using a loop, as show below:

imageCollection = [];

constructor() {
  for (let i = 0; i < 100; i++) {
      const url = 'http://via.placeholder.com/500x450?text=Image No ' + (i + 1);

this.imageCollection[i] = {
        url: url,
        show: false
    };
  }
}

As you can see, our image collection has 2 properties:  URL and show properties. The show property determines whether the image will be displayed. When the image comes into view, then we will toggle the show property to true and then the image shall be loaded. Inside our template, we will loop through the image collection. Then we shall use deferLoad directive from the ng-defer-load library to check whether the image is in view. If in view, it shall set the show property to true and hence display the image.

<div
  *ngFor="let image of imageCollection"
  (deferLoad)="image.show = true"
  style="height: 550px;"
>
  <ng-container *ngIf="image.show">
    <img [src]="image.url" width="100%" />
  </ng-container>
</div>

NB: To avoid many images being loaded initially, it is important to assign the image container a fixed height. And that’s it, you are now able to lazy load images instead of preloading all of them initially. https://youtu.be/ODSJZN7IKys

Source Code

You can get this source code here.

Final Thoughts

ng-defer-load uses Intersection Observer to detect which items are on view. While this is not available on all browsers, it uses poly fills to fallback to scroll detection mechanism, thus ensuring it is compatible with older browsers. Thank you for getting this far on this post, if you enjoyed it, please share it and if I left out something, you can remind me on the comment section below. Here are some more posts on lazy loading and performance optimization of your angular application.

  1. Lazy Loading of Angular Components and Modules
  2. Lazy loading of Styles and Scripts
  3. Introduction to Progressive Web Apps