Skip to content

RESOURCES / BLOG

lazy loading videos in NuxtJS

To improve the performance of your application and save your system’s resources while loading images or videos we need to use the technique lazy-loading.This is the practice of deferring the loading or initialization of resources or objects until they are actually needed. In this Mediajam, we are going to learn how to use nuxt.js a web application framework based on Vue.js to lazy-load images and videos.

There are multiple approaches to achieve lazy loading which include:

  • The HTML Image tag currently supports the loading attribute with the following values: eager, lazy
  • The Intersection Observer API allows you to asynchronously monitor changes in a target element’s intersection with an ancestor element or with the viewport of a top-level document.

You may use plugins to lazy load media without the increased complexity of interacting with the browser-level APIs. In this session, we shall be using the nuxt-lazy-load package to achieve lazy-loading of images and videos as illustrated in the codesandbox below :

The corresponding GitHub repository can be found here

To get started with the project, some knowledge of Javascript and Vue.js is recommended.

To scaffold a Nuxt.Js application make sure you have npx installed on your working environment then run the following command :

    yarn create-nuxt-app lazy-loading

The command above will ask you a series of questions to set up your application. Once all questions are answered it will install all the dependencies. We recommend using the following defaults for this project:

  • Programming Language -> JavaScript
  • Package manager -> Yarn
  • UI Framework -> TailwindCSS
  • Nuxt.Js modules -> Axios
  • Rendering mode: Universal (SSR/SSG)
  • Deployment target: Server (Node.Js hosting)

After installation, navigate to the project folder and launch it :

    yarn dev

The command above will ask you a series of questions to set up your application. Once all questions are answered it will install all the dependencies.

 ┣ 📂.nuxt
 ┣ 📂assets
 ┃ ┗ 📜README.md
 ┣ 📂components
 ┃ ┣ 📂VideoGrid
 ┃ ┃ ┗ 📜LazyVideo.vue
 ┃ ┣ 📜Hero.vue
 ┃ ┣ 📜ImageGrid.vue
 ┃ ┣ 📜README.md
 ┃ ┗ 📜VideoGrid.vue
 ┣ 📂layouts
 ┃ ┣ 📜README.md
 ┃ ┗ 📜default.vue
 ┣ 📂middleware
 ┃ ┗ 📜README.md
 ┣ 📂node_modules
 ┣ 📂pages
 ┃ ┣ 📜README.md
 ┃ ┗ 📜index.vue
 ┣ 📂plugins
 ┃ ┗ 📜README.md
 ┣ 📂static
 ┃ ┣ 📜README.md
 ┃ ┗ 📜favicon.ico
 ┣ 📂store
 ┃ ┣ 📂modules
 ┃ ┃ ┗ 📜boards.js
 ┃ ┣ 📜README.md
 ┃ ┗ 📜index.js
 ┣ 📜.env
 ┣ 📜.env.example
 ┣ 📜.gitignore
 ┣ 📜.prettierrc
 ┣ 📜README.md
 ┣ 📜nuxt.config.js
 ┣ 📜package-lock.json
 ┣ 📜package.json
 ┗ 📜yarn.lock

We shall briefly explain the directories we’ll be working on within this tutorial:

  • The components folder holds our custom Vue.Js components. You may organize the sub-directories as you wish.

  • The pages directory contains our application views. .vue files in this folder are automatically converted to routes.

  • The store directory contains our Vuex Store files. Vuex is a state management pattern + library for Vue.js applications. We enable Vuex by creating an index.js file.

As we shall be using Cloudinary to fetch all the images and videos to our project, the next step is to install and configure it into the application :

yarn add @nuxtjs/cloudinary
Code language: CSS (css)

After the installation is done, in the nuxt.config.js file add the following to configure the installed Cloudinary package :

  modules: [
    ...
    '@nuxtjs/cloudinary',
    ...
  ],

Code language: JavaScript (javascript)

Once the installation and the configuration is done, create a .env file and add the following cloudinary environment variables :

CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=

To inject the environment variables safely into the application, add the following to the nuxt.config.js file.

  privateRuntimeConfig: {
    cloudinaryCloudName: process.env.CLOUDINARY_CLOUD_NAME,
    cloudinaryApiKey: process.env.CLOUDINARY_API_KEY,
    cloudinaryApiSecret: process.env.CLOUDINARY_API_SECRET,
  },
Code language: CSS (css)

The above runtime config allows passing of dynamic config and environment variables to the nuxt context

To finalize adding Cloudinary to the application add cloudinary’s configuration to the nuxt.config.js file :

  cloudinary: {
    cloudName: process.env.CLOUDINARY_CLOUD_NAME,
    useComponent: true
  },
Code language: CSS (css)

We shall depend on the nuxt-lazy-load to load all our assets. Run the following command to install it :

yarn add nuxt-lazy-load

Then let’s set up the package in the configuration file to utilize it in the application :

  modules: [
    ...
    'nuxt-lazy-load'
    ...
  ],
Code language: JavaScript (javascript)

Upload images and videos to your Cloudinary account and add tags to them for easy fetching of specific assets(images/videos) via the admin API.

To ensure your API keys and Secret are kept safe and away from your front-end application, We shall perform the API calls via our vuex store.

Vuex is a Vue.js application state management pattern and library. It acts as a centralized repository for all the application’s components, with rules ensuring that the state can only be changed in predictable ways. It also works with Vue’s official dev tools extension to offer advanced features like zero-config time-travel debugging and state snapshot export/import.

In NuxtJs, the nuxtServerInit method is called only on the server-side when it is defined in the store\index.js and the mode is set up to universal.

export const actions = {
    async nuxtServerInit({ dispatch }) {
        // Trigger load action in boards module
        await dispatch('modules/boards/load')
    }
}
Code language: JavaScript (javascript)

This will trigger (dispatch) the load action in the modules\boards.js module.

After configuring the application state, the next step is to set the tags we want to use in the application then fetch the set the images and videos lists by their tags from the Cloudinary API in the modules\boards.js file as shown below :

// We set the tags we want to obtain
export const state = () => ({
  boards: [
    { name: 'cars', images: [], videos: [] },
    { name: 'houses', images: [], videos: [] },
    { name: 'vacation', images: [], videos: [] },
  ],
});

// Fetch and set image and video list by tag from cloudinary API
export const actions = {
  async load({ state }) {
    const cloudName = this.$config.cloudinaryCloudName;

    const axios = this.$axios;

    const boards = state.boards;

    for (const index in boards) {
      const imageUrl = `http://res.cloudinary.com/${cloudName}/image/list/${boards[index].name}.json`;

      const images = await axios.$get(imageUrl);

      boards[index].images = images.resources;

      const videoUrl = `http://res.cloudinary.com/${cloudName}/video/list/${boards[index].name}.json`;

      const videos = await axios.$get(videoUrl);

      boards[index].videos = videos.resources;
    }

    state.boards = boards;
  },
};
Code language: JavaScript (javascript)

Let’s now display each of the boards we have. For each we’ll display the following elements:

  • Hero image (Random banner image) + title
  • Images
  • Videos

This action is performed in the pages/index.vue component.

<div v-for="board in this.$store.state.modules.boards.boards" :key="board.name">
  <Hero :board="board" />
  <ImageGrid :board="board" />
  <VideoGrid :board="board" />
</div>
Code language: HTML, XML (xml)

The above code loops through the boards we have set in our modules\boards.js vuex module. For each board, we will display the hero section, image grid, and video grid.

To lazy-load the hero image, we’ll first create a computed property in the Hero.js file found in the components directory which will get a random image from the images in the board and return the image url using the Cloudinary SDK as illustrated below :

  computed: {
    backgroundUrl() {
      // Get a random image from the images in the board
      const image =
        this.board.images[Math.floor(Math.random() * this.board.images.length)]

    // Return the image url using the cloudinary SDK
      return this.$cloudinary.image.url(image.public_id, {
        class: '...',
        width: '1920',
        height: '288',
        gravity: 'auto:subject',
        fetchFormat: 'auto',
        quality: 'auto',
        crop: 'fill',
      })
    },
  },
Code language: JavaScript (javascript)

Once the background image url is fetched from Cloudinary, we then lazy load it using the nuxt-lazy-load package. We do this by adding a lazy-background attribute to the div.

<div class=".." :lazy-background="backgroundUrl">
 ...
</div>
Code language: HTML, XML (xml)

To lazy load all the tagged images, we will first loop through all the images in the board after which we will use Cloudinary’s vue.js cld-image component to display the fetched images. Using the loading attribute on the cld-image component, we can lazy load all the images.

   <div v-for="image in board.images" :key="image.public_id">
     <cld-image
      :public-id="image.public_id"
       width="200"
       height="200"
       crop="fill"
       gravity="auto:subject"
       fetchFormat="auto"
       quality="auto"
       class="mb-1 border-solid w-full hover:border-yellow-500"
      :alt="`${image.public_id} image`"
       loading="lazy"
    />
 </div>
Code language: HTML, XML (xml)

In order to lazy-load videos, on the app we’ll first loop through all videos from cloudinary in the VideoGrid.vue file found in the components directory :

<div v-for="video in board.videos" :key="video.public_id">
    <lazy-video
     :video="video"
      class="mb-1 border-solid w-full hover:border-yellow-500"
    />
</div>
Code language: HTML, XML (xml)

The next step is to compute all the videos urls and thumbnail urls from cloudinary’s API:

  computed: {
    videoUrl() {
      return this.$cloudinary.video.url(this.video.public_id, {
        controls: true,
        crop: 'fill',
        format: 'mp4',
        width: 200,
        height: 200,
        quality: 'auto',
      })
    },

    thumbnailUrl() {
      const { url } = this.$cloudinary.video.thumbnail(this.video.public_id, {
        version: this.video.version,
        crop: 'fill',
        width: 200,
        height: 200,
      })
      return url
    },
  },
Code language: JavaScript (javascript)

We’ll leverage the v-lazy-load attribute on HTML video element to lazy load videos. You may also specify the poster using the data-poster attribute.

  <video
    class="..."
    :data-poster="thumbnailUrl"
    autoplay
    muted
    v-lazy-load
  >
    <source :data-src="videoUrl" type="video/mp4" />
    Your browser does not support the video tag.
  </video>
Code language: HTML, XML (xml)

With all the above when you visit your browser’s network tab you will realize both images and videos will load only when you scroll the page(on demand).

We have seen the importance and how we can lazy-load assets in nuxt.js for improved application and web performance. Nuxt.js offers numerous capabilities and possibilities on how one can increase their application’s speed through lazy-loading components.

Start Using Cloudinary

Sign up for our free plan and start creating stunning visual experiences in minutes.

Sign Up for Free