Rendering items on the DOM individually can cause a significant performance lag for users, especially when they’re scrolling through large lists. To make scrolling more efficient, we should use a virtual scroll list, which increases page load speed and prevents the web application from stuttering.
A virtual scroll list is similar to a standard scroll list, however, only the data in the user’s current view is rendered at any moment. As a user scrolls down a page, new items are rendered as older items are removed.
In this article, we’ll explore vue-virtual-scroll-list
, an amazing library for creating virtual scroll lists in Vue.js. Let’s get started!
vue-virtual-scroll-list
The vue-virtual-scroll-list
library has two primary methods for rendering a webpage’s content into a list, item
mode and v-for
mode.
item
mode is ideal for rendering static content. Once content is appended on the DOM, item
mode frees up the memory that was being used. If you change the data, you’ll need to call forceRender()
and start the process again.
To render dynamic content, the better choice is v-for
mode. In v-for
mode, the data provided to the list is referenced inside the memory. Therefore, when the data changes, list items are re-rendered and the context is maintained.
Let’s take a closer look at the vue-virtual-scroll-list
library by comparing the performance of item
mode with and without using a virtual scroll list.
First, we’ll set up a new Vue.js project and install vue-virtual-scroll-list
. Then, we’ll create a list using randomly generated data. Finally, we’ll render our list with and without virtual scrolling, comparing the performance of each.
First, make sure you have Vue.js installed on your machine. Create a new Vue.js project with the following command:
vue create virtual-scroll-demo
Once the project is set up, install the vue-virtual-scroll-list
library:
npm install vue-virtual-scroll-list --save
Now, our project has the following structure:
Now that we have the base for our project set up, let’s get started on the foundation for creating both of our lists.
Navigate to your /src
folder and create a file called data.js
. Let’s add the following simple function that generates random data to data.js
:
let idCounter = 0; export function getData(count) { const data = []; for (let index = 0; index < count; index++) { data.push({ id: String(idCounter++), text: Math.random() .toString(16) .substr(10), }); } return data; }
Next, we’ll create a new file called Item.vue
, which is the item
component that we’ll render. In Item.vue
, we’ll include the following code block, which creates a template and styling for our list, as well as props that retrieve and display the data generated above:
<template> <div class="item"> <div class="id">{{ source.id }} - {{ source.text }}</div> </div> </template> <script> export default { name: 'item', props: { source: { type: Object, default() { return {} } } } } </script> <style scoped> .item { display: flex; flex-direction: column; border-bottom: 1px solid lightgrey; padding: 1em; } </style>
Now that we have a list created, let’s render the list items on our DOM without using the vue-virtual-scroll-list
. Add the following code to App.vue
:
<template> <div id="app"> <div class="wrapper"> <div class="list"> <p v-for="item in items" :key="item"> {{item}} </p> </div> </div> </div> </template> <script> import Item from './Item' import { getData } from './data' export default { name: 'App', data() { return { item: Item, items: getData(100000) } } } </script> <style> #app { text-align: center; color: #2c3e50; margin-top: 1em; padding: 1em; } .list { border: 2px solid red; border-radius: 3px; } </style>
In the code block above, we rendered 100,000 items into our DOM. Let’s see how our list will perform with this much data and no virtual scroll. Start the project with the following npm command:
npm run serve
We’ll get the following output:
When we check the inspect
element in the browser, we’ll see that all of the HTML elements have been appended to the browser DOM, as seen in the image below:
Appending elements in the browser DOM will increase the DOM’s size. Therefore, the browser will require more time to append each item to the DOM, potentially causing a significant performance lag. Let’s look closely at the amount of time it took the browser to append our list to the DOM:
The event DOMContentLoaded
fired after 22 seconds, meaning the browser tab required 22 seconds to load before displaying the final rendered list. Similarly, as seen in the image below, rendering our list consumed 128 MB of memory:
Now, let’s try rendering our list using a virtual scroll. Import the vue-virtual-scroll-list
package in main.js
:
import Vue from "vue"; import App from "./App.vue"; Vue.config.productionTip = false; import VirtualList from "vue-virtual-scroll-list"; Vue.component("virtual-list", VirtualList); new Vue({ render: (h) => h(App), }).$mount("#app");
Next, we’ll render the data for the items inside the virtual-list
component. Let’s change our App.js
file to look like the following code block:
<template> <div id="app"> <div class="wrapper"> <virtual-list class="list" style="height: 360px; overflow-y: auto;" :data-key="'id'" :data-sources="items" :data-component="item" :estimate-size="50" /> </div> </div> </template> <script> import Item from './Item' import { getData } from './data' export default { name: 'App', data() { return { item: Item, items: getData(100000) } } } </script> <style> #app { text-align: center; color: #2c3e50; margin-top: 1em; padding: 1em; } .list { border: 2px solid red; border-radius: 3px; } </style>
Note that the data props are required for the virtual list to render the items. Running the code block above will give us the following output:
We can see in the image below that only a few items are rendered at one time. When the user scrolls down, newer items are rendered:
Now, our DOM tree is much smaller than before! When we render our virtual scroll list, DOMContentLoaded
will fire much faster than before!
As seen in the image above, the event fired in only 563 milliseconds. Similarly, our operation consumed only 79 MB of memory, which is much less than when we didn’t use a virtual scroll.
Now you know how to create a virtual scrolling list in Vue.js using the vue-virtual-scroll-list
library!
In this tutorial, we created a static list that uses randomly generated data, then implemented it in our Vue.js application, comparing its performance with and without using a virtual scroll.
Virtual scrolling lists are highly performant, especially when you have a large list of items on your webpage. Using a virtual scroll list can increase the page load speed and improve the user experience overall!
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowValidating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.
Optimize search parameter handling in React and Next.js with nuqs for SEO-friendly, shareable URLs and a better user experience.
Learn how Remix enhances SSR performance, simplifies data fetching, and improves SEO compared to client-heavy React apps.