00

Angular Directive to Lazy Load Images

Sunday, January 10, 2021

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.

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

npm i lazysizes

Instructions

Do

Use lazysizes to lazy-load image that are offscreen.

Why

Lazy loading of resources that are not immediately necessary for the page render improves TTI and FCP.

Code Examples

lazy.directive.ts

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');
  }
}

user.component.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"
/>
Brian Love

I am a software engineer and Google Developer Expert in Web Technologies and Angular with a passion for learning, writing, speaking, teaching and mentoring. I regularly speaks at conferences and meetups around the country, and co-authored "Why Angular for the Enterprise" for O'Reilly. When not coding, I enjoy skiing, hiking, and being in the outdoors. I started lookout.dev to break down the barriers of learning in public. Learning in public fosters growth - for ourselves and others.

Google Developers Expert

Have a question or comment?