Tutorial

Performant Lists and Tables in Vue.js with vue-virtual-scroller

Published on April 9, 2018
Default avatar

By Alex Jover Morales

Performant Lists and Tables in Vue.js with vue-virtual-scroller

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

As a developer, you’ve probably had to implement a big list or table for an app, either by loading lots of data without pagination, or using an infinite scroll pagination and scrolling several times through it. These lists or tables can become slow, especially when they have lots of elements, CSS styles and/or transitions.

vue-virtual-scroller is a Vue.js plugin that applies the virtual scroll technique in order to performantly render a list to the DOM, whether it’s a plain HTML ul > li list, a table, or a customized list.

Setup vue-virtual-scroller

After you have a basic Vue.js project created, start by installing the plugin from npm:

$ npm install -D vue-virtual-scroller

Then, in your main.js file you must include its CSS file and initialize it:

main.js
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
import Vue from "vue";
import VueVirtualScroller from "vue-virtual-scroller";

Vue.use(VueVirtualScroller);
// ...

That should be enough to start using it.

Creating a VirtualList

To start with a simple example, let’s create a plain list that takes lots of data and uses vue-virtual-scroller to render it.

Let’s use JSON-Generator to generate the JSON for 5000 entries and save it to a data.json file. You can use the following structure:

[
  '{{repeat(5000)}}',
  {
    _id: '{{objectId()}}',
    age: '{{integer(20, 40)}}',
    name: '{{firstName()}} {{surname()}}',
    company: '{{company().toUpperCase()}}'
  }
]

Let’s create a VirtualList.vue file, where we import data.json and add it to an items component state property. Then, use the <virtual-scroller> component, passing those items:

VirtualList.vue
<template>
  <virtual-scroller :items="items" item-height="40" content-tag="ul">
    <template slot-scope="props">
      <li :key="props.itemKey">{{props.item.name}}</li>
    </template>
  </virtual-scroller>
</template>

<script>
import items from "./data.json";

export default {
  data: () => ({ items })
};
</script>

We must set an item-height to the virtual-scroller component. Alternatively, since we’re creating a list, we’ve set the content-tag="ul" which will wrap the contents into a <ul> tag.

vue-virtual-scroller allows to use scoped slots to render the contents for full flexibility with the rendering. By using slot-scope="props" we have access to the virtual-scroller exposed data, and use it for rendering.

props contains an itemKey property that we should bind using :key="props.itemKey" on the root of the content for performance reasons. Then we can access props.item containing the raw item from the JSON we’ve passed through the items property to virtual-scroller.

If you’d like to style the elements in the list, you can add a class attribute to the virtual-scroller component, and access its contents:

<template>
  <virtual-scroller class="virtual-list" ...></virtual-scroller>
</template>

<style>
.virtual-list ul {
  list-style: none;
}
</style>

Or, with scoped styles, use the /deep/ selector:

<style scoped>
.virtual-list /deep/ ul {
  list-style: none;
}
</style>

Creating a VirtualTable

Similar to the VirtualList component, let’s create a VirtualTable.vue file for a table:

VirtualTable.vue
<template>
  <virtual-scroller :items="items" item-height="40" content-tag="table">
    <template slot-scope="props">
      <tr :key="props.itemKey">
        <td>{{props.item.age}}</td>
        <td>{{props.item.name}}</td>
        <td>{{props.item.company}}</td>
      </tr>
    </template>
  </virtual-scroller>
</template>

<script>
import items from "./data.json";

export default {
  data: () => ({ items })
};
</script>

We encounter a problem with this example; we want to add a <thead> tag as the table header in order to show the columns names: Age, Name and Company.

Fortunately, virtual-scroller distributes its internal content using the following slots structure:

<main>
  <slot name="before-container"></slot>
  <container>
    <slot name="before-content"></slot>
    <content>
      <!-- Your items here -->
    </content>
    <slot name="after-content"></slot>
  </container>
  <slot name="after-container"></slot>
</main>

Any of those slots can be used to place contents in there. container will be replaced by the tag value of the container-tag property, by default div, and the content by the content-tag value.

It’s therefore easy to add a thead using the before-content slot:

<template>
  <virtual-scroller
    :items="items"
    item-height="40"
    container-tag="table"
    content-tag="tbody"
    >
      <thead slot="before-content">
        <tr>
          <td>Age</td>
          <td>Name</td>
          <td>Company</td>
        </tr>
      </thead>
      <template slot-scope="props">
        <tr :key="props.itemKey">
          <td>{{props.item.age}}</td>
          <td>{{props.item.name}}</td>
          <td>{{props.item.company}}</td>
        </tr>
      </template>
  </virtual-scroller>
</template>

Notice that we’ve changed content-tag="table" for container-tag="table" and content-tag="tbody" in order to keep a usual table structure, since now we’re using more elements.

I’m sure you can imagine how to add a tfoot element as well 😉.

Wrapping Up

We’ve used the vue-virtual-scroller plugin to create components for a VirtualList and a VirtualTable. If you try them out with the 5000 items we’ve generated, they should render and scroll quite smoothly. Check vue-virtual-scroller docs to see more options and customizations.

You can see the working code of the article in this Codesandox.

Stay cool 🦄

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Alex Jover Morales

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
1 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Helpful. However, this no longer work on the latest version where the maintainer has removed support for table element.

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel