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-placeholder2# OR3npx create-nuxt-app nuxtjs-low-quality-placeholder4# OR5npm 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-placeholder23yarn dev4# OR5npm 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.
- ali-karimiboroujeni-XAwyrABB2IA-unsplash.jpg
- ryan-ancill-aJYO8JmVodY-unsplash.jpg
- pawel-czerwinski-DKzsQT9zUPk-unsplash.jpg
- nathan-dumlao-f5DHtik4hWc-unsplash.jpg
- elise-wilcox-Q6GRqKGuswc-unsplash.jpg
- toa-heftiba-FV3GConVSss-unsplash.jpg
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/cloudinary2# OR3npm run install @nuxtjs/cloudinary
Add @nuxtjs/cloudinary
as a module in the nuxt.config.js
file.
1// nuxt.config.js2export 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.js2export default {3 cloudinary: {4 cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,5 useComponent: true,6 secure: true7 }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.vue2<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 <Img7 :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.vue2<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 --save2#or3yarn 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.vue2<script>34import BlurLoader from 'vue-blur-loader'56export default {7 ...8 components:{9 BlurLoader10 },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 <BlurLoader8 :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.