Lazy Loading Images In Angular

If you’ve ever run a website through a pagespeed analyzer like Google’s Site Speed or GTMetrix, you’ve probably seen a recommendation that you should lazy load images below the fold. That is, when your website first loads, only show images that are actually visible on screen, then as you scroll, load additional images as you go.

The interesting thing about this is that much of the guides out there will focus on javascript snippets that utilize something like swapping a data-src attribute to the src attribute of an image based on where the page is currently scrolled to. While this works, it can often lead to weird loading phases where you see the image flash while you scroll over it. Not ideal!

But browsers now support lazy loading out of the box. Meaning that there’s no javascript snippets required! Just a small attribute on an image and the browser will take care of when the image should be loaded! Often this leads to a much smoother experience (And one that the browser has obviously optimized for).

Lazy Loading Attribute

To lazy load an image, simply add the loading=”lazy” attribute like so :

<img src="example.png" loading="lazy"/>

Notice how the src attribute is still set like normal? That’s important because it provides backwards compatibility for any browser that doesn’t support Lazy Loading. If we check caniuse.com however : https://caniuse.com/?search=lazy%20loading there is actually great support and it’s really only old browsers such as IE, and Safari that haven’t quite implemented it yet.

Seems easy right? But what if you just want to add lazy loading to every image on your site, and not have to go back and manually add it everywhere?

Add Lazy Loading Globally

If we want to set all images to be lazy loaded by default, then it’s actually rather easy using Angular Directives. Something like so :

import { Directive, ElementRef } from '@angular/core';
@Directive({
    selector: 'img:not([loading])'
})
export class LazyLoadImagesDirective {
  constructor(el : ElementRef) { 
    el.nativeElement.setAttribute('loading','lazy')
  }
}

This simply takes all images, and adds the loading=lazy attribute. Again, if the browser doesn’t support lazy loading it really isn’t a big deal, the directive still runs and any legacy users won’t see a difference.

I’ll also note a key important detail here, and that is our selector for our directive very specifically is :

@Directive({
    selector: 'img:not([loading])'
})

What this actually says is run this directive on all img tags that *do not* already have a loading attribute set. This is not because it might run twice on the same element, but because it allows us to override the directive and still eager load images in the rare cases we don’t want them lazy loaded.

For example, the directive will not run on the following element :

<img src="example.png" loading="eager"/>

Leave a Reply

Your email address will not be published.