Lazy Loading Images Angular - Increase Performance Without Libraries
In this post you will learn how to implement lazy loading of images inside Angular.
Do we have a problem?
Just imagine that we have a website and we typically throw lots of image tags inside our markup to load images. This is completely fine with your internet at home which is blazingly fast. But it is not fine for some slow mobile network. Obviously we want to optimize our images to make sure that they are being loaded in the best possible way and work even with slow internet.
Here for our project I prepared several big images which are really heavy to load. This is one of them.
I also prepared the same image but made them only 20px size. It looks like an extremely blurry version of original image. We will render these small images which can be loaded fast first and only after our big image is being loaded we will show it instead.
Some of the big images are 5mb each but 20px smaller alternative is only 500 bytes.
Small images
The next question is how to generate such small images. First way is to use any online project which allows you to compress images. Another possible variant is to use ffmpeg
tool to generate it.
ffmpeg - i src/assets/image-5.jpeg -vf scale=20:-1 src/assets/image-5-small.jpeg
Here we are scaling our original image to 20px and save it with another name.
The next step that I want to do is to implement an additional Angular component which will implement for us progressive image loading.
// src/app/progressiveImage/progressiveImage.component.html
{{imageUrlSmall}}
{{imageUrl}}
Here we render both inputs that we want to provide to the component.
@Component({
selector: 'progressive-image',
templateUrl: './progressiveImage.component.html',
styleUrls: ['./progressiveImage.component.css'],
standalone: true,
imports: [CommonModule],
})
export class ProgressiveImageComponent {
@Input({ required: true }) imageUrl!: string;
@Input({ required: true }) imageUrlSmall!: string;
}
We created a progressive-image
component where we will provide a url to big and small image.
// src/app/app.component.html
<div class="image-container">
<div class="image-item">
<progressive-image
imageUrl="/assets/image-1.jpeg"
imageUrlSmall="/assets/image-1-small.jpeg"
></progressive-image>
</div>
<div class="image-item">
<progressive-image
imageUrl="/assets/image-2.jpeg"
imageUrlSmall="/assets/image-2-small.jpeg"
></progressive-image>
</div>
<div class="image-item">
<progressive-image
imageUrl="/assets/image-3.jpeg"
imageUrlSmall="/assets/image-3-small.jpeg"
></progressive-image>
</div>
<div class="image-item">
<progressive-image
imageUrl="/assets/image-4.jpeg"
imageUrlSmall="/assets/image-4-small.jpeg"
></progressive-image>
</div>
<div class="image-item">
<progressive-image
imageUrl="/assets/image-5.jpeg"
imageUrlSmall="/assets/image-5-small.jpeg"
></progressive-image>
</div>
</div>
Here I rendered progressive-image
component for every image that we have.
As you can see we successfully renders all urls in our component.
Now lets try to render images
// src/app/progressiveImage/progressiveImage.component.html
<div>
<img [src]="imageUrl" class="image"/>
</div>
This is how we typically render images without any optimization. Additionally as you can see we didn't try to specify any size because we want to make the component as flexible as possible.
// src/app/progressiveImage/progressiveImage.component.css
.image {
display: block;
width: 100%;
}
This is why we set our image width to 100%. Now let's style it from the outside.
// src/app/app.component.css
.image-container {
width: 500px;
margin: 0 auto;
}
.image-item {
margin-bottom: 20px;
}
Here we provided a fixed width and margins for our elements.
Now our images look amazing like on Instagram for example.
Improving performance
Let's start with improving our performance. The easiest step is to load all our images with lazy
attribute. It means that browser will load these images when it has resources for that. So first of all we are rendering the whole page and only then our images.
// src/app/progressiveImage/progressiveImage.component.html
<div>
<img [src]="imageUrl" class="image" loading="lazy"/>
</div>
You can safely use loading="lazy"
in all modern browsers.
In order to test the speed of the loading we must lower the speed of the internet. Here I selected Slow 3G
which shows exactly how our website work on the slow internet. As you can see with 3G all these images were loading long time. All our big images take more that 20 seconds to load.
Lazy is not a good solution for a slow internet
Rendering small images
This is why our next step is to render small images before our big images are ready.
<div
[ngStyle]="{ 'background-image': 'url(' + imageUrlSmall + ')' }"
[ngClass]="{ placeholder: true, 'placeholder-loaded': isLoaded }"
>
<img
[src]="imageUrl"
[ngClass]="{ image: true, 'image-loaded': isLoaded }"
loading="lazy"
(load)="onImageLoad()"
/>
</div>
Here we render a small image on the div and it covers fully our image.
export class ProgressiveImageComponent {
...
isLoaded = false;
onImageLoad() {
this.isLoaded = true;
}
}
We also added onImageLoad
which is called when our big images is fully loaded. It sets isLoading
property to false so we render a big image instead of small one.
.image {
display: block;
width: 100%;
opacity: 0;
}
.placeholder {
background-repeat: no-repeat;
background-size: cover;
filter: blur(10px);
}
.image-loaded {
opacity: 1;
}
.placeholder-loaded {
filter: none;
}
We hided our big image with opacity: 0
until it is fully loaded. To our small placeholder image we added a blur filter to make it more realistic. When image is fully loaded we make it visible again.
This is how our blurred small image looks like. User can understand how finished image looks like and it loads blazingly fast.
Here is how it look when some of our images are already loaded and some not.
Want to conquer your next JavaScript interview? Download my FREE PDF - Pass Your JS Interview with Confidence and start preparing for success today!
📚 Source code of what we've done