Lazy Loading Images Using Low-quality Placeholders

Eugene Musebe

Overview

Ensuring our web pages load quickly is our duty as builders of the internet. By lazy loading our images, we are able to save valuable load time for our users. The challenge with lazy loading is pre-generating and storing low-quality placeholders for the full quality image. Let us learn how to generate the placeholders on-demand low-quality image placeholders.

Codesandbox

The final project can be viewed on Codesandbox.

You can find the full source code on my Github repository.

Preparation

Nuxt.Js Project

Nuxt.Js is a Vue.Js framework that allows us to build our applications with confidence. To get started, ensure you have Yarn, NPM v5.2+ or v6.1+ installed. Open the terminal in your preferred working directory and run the following command.

1yarn create nuxt-app nuxtjs-low-quality-placeholder
2# OR
3npx create-nuxt-app nuxtjs-low-quality-placeholder
4# OR
5npm init nuxt-app nuxtjs-low-quality-placeholder

This will initiate some questions to help set up the project. Here are our recommended defaults:

Project name: nuxtjs-low-quality-placeholder Programming language: JavaScript Package manager: Yarn UI framework: Tailwind CSS Nuxt.Js modules: None Linting tools: None Test frameworks: None Rendering mode: Universal (SSR / SSG) Deployment target: Server (Node.js hosting) Development tools: None What is your Github username? < your-github-username >

Once the setup is complete, you may now enter and run your project. It will be accessible on http://localhost:3000.

1cd nuxtjs-low-quality-placeholder
2
3yarn dev
4# OR
5npm run dev

Cloudinary account

Cloudinary is a media management platform that allows us to build engaging and customized visual experiences. Let us prepare our account for the project. If you do not have an account, feel free to register here.

Proceed to the media library section and create a folder named nuxtjs-low-quality-placeholder. Add the following files to the folder.

You should now have a folder similar to this one.

@nuxtjs/cloudinary installation

To allow our application to communicate with Cloudinary, we are going to install @nuxtjs/cloudinary. Run the following command in the root project folder.

1yarn add @nuxtjs/cloudinary
2# OR
3npm run install @nuxtjs/cloudinary

Add @nuxtjs/cloudinary as a module in the nuxt.config.js file.

1// nuxt.config.js
2export default {
3 ...
4 modules: [
5 '@nuxtjs/cloudinary'
6 ]
7 ...
8}

Add a cloudinary section in the nuxt.config.js file for our configurations.

1// nuxt.config.js
2export default {
3 cloudinary: {
4 cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,
5 useComponent: true,
6 secure: true
7 }
8}

The above code refers to the NUXT_ENV_CLOUDINARY_CLOUD_NAME environmental variable. These are values we don't want to be stored in our codebase for either security or portability reasons. Let's create the .env file in the root project folder.

1touch .env

Then add your cloud name. This can be located on your Cloudinary dashboard.

1NUXT_ENV_CLOUDINARY_CLOUD_NAME=<your-cloudinary-cloud-name>

Displaying the images

Before optimizing the images, we first need to display them in the user interface. To do this, let us load them into our component state and generate the image path using the Cloudinary API.

1// pages/index.vue
2<script>
3export default {
4 name: 'IndexPage',
5 data(){
6 return {
7 images: [
8 {
9 public_id:'nuxtjs-low-quality-placeholder/ryan-ancill-aJYO8JmVodY-unsplash',
10 credit: 'Photo by <a href="https://unsplash.com/@ryanancill?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Ryan Ancill</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>',
11 },
12 {
13 public_id:'nuxtjs-low-quality-placeholder/ali-karimiboroujeni-XAwyrABB2IA-unsplash',
14 credit:'Photo by <a href="https://unsplash.com/@alikarimi_photography?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Ali Karimiboroujeni</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>',
15 },
16 {
17 public_id:'nuxtjs-low-quality-placeholder/nathan-dumlao-f5DHtik4hWc-unsplash',
18 credit:'Photo by <a href="https://unsplash.com/@nate_dumlao?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Nathan Dumlao</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>',
19 },
20 {
21 public_id:'nuxtjs-low-quality-placeholder/pawel-czerwinski-DKzsQT9zUPk-unsplash',
22 credit:'Photo by <a href="https://unsplash.com/@pawel_czerwinski?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Pawel Czerwinski</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>',
23 },
24 {
25 public_id:'nuxtjs-low-quality-placeholder/toa-heftiba-FV3GConVSss-unsplash',
26 credit:'Photo by <a href="https://unsplash.com/@heftiba?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Toa Heftiba</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>',
27 },
28 {
29 public_id:'nuxtjs-low-quality-placeholder/elise-wilcox-Q6GRqKGuswc-unsplash',
30 credit:'Photo by <a href="https://unsplash.com/@elise_outside?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Elise Wilcox</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>'
31 }
32 ]
33 };
34 },
35 methods:{
36 imagePath(publicId){
37 return this.$cloudinary.image.url(`${publicId}.jpg`);
38 }
39 }
40}
41</script>

We may now loop throw the images array and display them.

1<!-- pages/index.vue -->
2<template>
3 ...
4 <div v-for="image in images" :key="image.public_id">
5 <div>
6 <Img
7 :src='imagePath(image.public_id)'
8 :smallSrc='placeholderPath(image.public_id)'
9 :alt="credit"
10 />
11 </div>
12 <div>
13 <h3 v-html="image.credit"></h3>
14 </div>
15 </div>
16 ...
17</template>

Generating low-quality placeholders

To generate low-quality placeholders, we are going to use the cartoonify transformation. We specify a color reduction of 2 and a detail reduction to 0.1.

After cartooning the image, we scale it down to a width of 150 pixels.

1// pages/index.vue
2<script>
3export default {
4 ...
5 methods:{
6 ...
7 placeholderPath(publicId){
8 return this.$cloudinary.image.url(
9 `${publicId}.svg`,
10 {
11 effect: "cartoonify:colors:2:detail:0.1",
12 width: 150,
13 crop:'scale'
14 }
15 );
16 }
17 }
18}
19</script>

Lazy loading the images

Now that we now have a high quality and a low-resolution version of each photo, we need to implement lazy loading. We will make use of the vue-blur-loader package for this. To get started, let's install the package.

1npm install vue-blur-loader --save
2#or
3yarn add vue-blur-loader

Let us not use the BlurLoader component to lazy load the images. First, let's load the component into the page.

1// pages/index.vue
2<script>
3
4import BlurLoader from 'vue-blur-loader'
5
6export default {
7 ...
8 components:{
9 BlurLoader
10 },
11 ...
12}
13</script>

We can now replace the img tag we had early with the BlurLoader component injecting both image sources into the component.

1<!-- pages/index.vue -->
2<template>
3 ...
4 <div v-for="image in images" :key="image.public_id">
5 <div>
6 <div>
7 <BlurLoader
8 :src='imagePath(imagde.public_id)'
9 :smallSrc='placeholderPath(image.public_id)'
10 :alt="credit"
11 />
12 </div>
13 <div>
14 <h3 v-html="image.credit"></h3>
15 </div>
16 </div>
17 </div>
18 ...
19</template>

Our app should now automatically lazy load the low-quality placeholder before the high-resolution image.

To learn more about the transformations available, feel free to go through the transformation URL API reference.

Eugene Musebe

Software Developer

I’m a full-stack software developer, content creator, and tech community builder based in Nairobi, Kenya. I am addicted to learning new technologies and loves working with like-minded people.