Lazy Load Images with Angular
Tags: webperf, angular
Published: 2021-01-26 23:05:17

I order to improve a page's Time to Interactive (TTI) and First Contentful Paint (FCP) I created an Angular directive that uses the popular lazysizes JavaScript library to lazy load images.

## Install lazysizes

To get started, you need to first install the library via npm (or yarn):

```bash
npm i lazysizes
```

## `LazyDirective`

Next, create a new lazy.directive.ts file:

```typescript
import { AfterViewInit, Directive, ElementRef, Input, Renderer2 } from '@angular/core';

import 'lazysizes';
import 'lazysizes/plugins/unveilhooks/ls.unveilhooks';

// tslint:disable:no-input-rename
@Directive({
  selector: '[appLazy]'
})
export class LazyDirective implements AfterViewInit {
  /** The native element. */
  el: HTMLElement | null = null;

  /** The HTMLElement background-image value. */
  @Input('data-bg') dataBg: string | null = null;

  /** The HTMLImageElement sizes attribute. */
  @Input('data-sizes') dataSizes: string | null = null;

  /** HTMLImageElement src attribute. */
  @Input('data-src') src: string | null = null;

  /** HTMLImageElement srcset attribute. */
  @Input('data-srcset') srcSet: string | null = null;

  /** A transparent gif. */
  transparent = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';

  constructor(private readonly elementRef: ElementRef, private readonly renderer: Renderer2) {}

  ngAfterViewInit(): void {
    if (!this.elementRef.nativeElement) {
      return;
    }
    this.el = this.elementRef.nativeElement;

    if (this.el.tagName.toUpperCase() === 'IMG') {
      (this.el as HTMLImageElement).src = this.transparent;
      if (this.dataSizes) {
        this.renderer.setAttribute(this.el, 'data-sizes', this.dataSizes);
      }
      if (this.src) {
        this.renderer.setAttribute(this.el, 'data-src', this.src);
      }
      if (this.srcSet) {
        this.renderer.setAttribute(this.el, 'data-srcset', this.srcSet);
      }
    } else {
      this.renderer.setStyle(this.el, 'background-image', `url(${this.transparent})`);
      if (this.dataBg) {
        this.renderer.setAttribute(this.el, 'data-bg', this.dataBg);
      }
    }
    this.renderer.addClass(this.el, 'lazyload');
  }
}
```

## Implement lazy loading

Finally, here is the implementation in a template:

```html
<!-- HTMLElement background -->
<div class="avatar" lktLazy [data-bg]="user.photoURL"></div>

<!-- HTMLImageElement src -->
<img lktLazy [data-src]="user.photoURL" [attr.alt]="user.displayName" />

<!-- HTMLImageElement srcset -->
<img 
  lktLazy 
  data-srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w" 
  [attr.alt]="user.displayName"
/>
```

## Learn in Public

If you're like me, you are constantly learning. Further, I love to share what I learn in public.

My biggest roadblock to learning in public is taking the time to write a long blogpost. So, I made [lookout.dev](https://lookout.dev) to make it easy to learn in public.

Checkout my other lookouts, along with the lookout to create an [Angular Directive to lazy load images with Angular](https://lookout.dev/rules/angular-directive-to-lazy-load-images).

Cheers!