DEV Community

Cover image for Handling large lists efficiently in Vue 3
Jakub Andrzejewski
Jakub Andrzejewski

Posted on

23 1 1

Handling large lists efficiently in Vue 3

Handling large lists in Vue 3 efficiently is crucial for maintaining smooth performance and a great user experience. When dealing with thousands (or even millions) of items, a naive rendering approach can slow down or crash the application.

In this article, we’ll explore the best techniques to optimize large lists in Vue 3.

Enjoy!

🤔 Common Performance Issues with Large Lists

Before diving into solutions, let's identify key performance problems:

  • Rendering Too Many Elements: The browser struggles to handle excessive DOM nodes.
  • Slow Re-renders: Frequent updates to large lists can cause slow performance.
  • Inefficient Event Handling: Using events like click or mouseover on every list item can cause excessive computation.
  • Memory Consumption: Storing too much data in memory can cause high RAM usage.

Now, let’s look at techniques to solve these issues.

🟢 Handling large lists efficiently in Vue

1. Use Virtual Scrolling for Large Lists

Instead of rendering the entire list, only visible items are rendered. As the user scrolls, items are dynamically loaded and unloaded. Let's take a look at the following example with VueUse’s useVirtualList:

<script setup>
import { ref } from 'vue';
import { useVirtualList } from '@vueuse/core';

const items = ref(Array.from({ length: 100000 }, (_, i) => `Item ${i + 1}`));
const { list, containerProps, wrapperProps } = useVirtualList(items, { itemHeight: 50 });
</script>

<template>
  <div v-bind="containerProps" style="height: 300px; overflow-y: auto;">
    <div v-bind="wrapperProps">
      <div v-for="item in list" :key="item.index" class="list-item">
        {{ item.data }}
      </div>
    </div>
  </div>
</template>

<style>
.list-item {
  height: 50px;
  display: flex;
  align-items: center;
  border-bottom: 1px solid #ccc;
}
</style>
Enter fullscreen mode Exit fullscreen mode

This efficiently displays only the items visible in the viewport.

2. Optimize v-for with key

When using v-for, always provide a unique key to help Vue optimize re-renders. Using an index as the key can cause unintended re-renders when modifying the list.

<li v-for="item in items" :key="item.id">{{ item.name }}</li>

<!-- ❌ Not Recommended -->
<li v-for="(item, index) in items" :key="index">{{ item.name }}</li>
Enter fullscreen mode Exit fullscreen mode

3. Use Lazy Loading for Data Fetching

Instead of loading all data at once, fetch items in small batches.

<script setup>
import { ref, onMounted } from 'vue';

const items = ref([]);
const page = ref(1);
const loading = ref(false);

const fetchMoreItems = async () => {
  if (loading.value) return;
  loading.value = true;

  // Simulating API call
  setTimeout(() => {
    for (let i = 1; i <= 20; i++) {
      items.value.push(`Item ${items.value.length + 1}`);
    }
    loading.value = false;
    page.value++;
  }, 1000);
};

onMounted(fetchMoreItems);
</script>

<template>
  <ul>
    <li v-for="item in items" :key="item">{{ item }}</li>
  </ul>
  <button @click="fetchMoreItems" :disabled="loading">
    Load More
  </button>
</template>
Enter fullscreen mode Exit fullscreen mode

This ensures that only the necessary data is loaded as needed.

4. Debounce Input Search for Filtering Lists

When filtering a large list based on user input, debounce the search to reduce unnecessary calculations. Let's take a look at the following example with VueUse's useDebounceFn:

<script setup>
<script setup>
import { ref, computed } from 'vue';
import { useDebounceFn } from '@vueuse/core';

const items = ref(Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`));
const searchQuery = ref('');

const filteredItems = computed(() => {
  return items.value.filter(item => item.toLowerCase().includes(searchQuery.value.toLowerCase()));
});

const updateSearch = useDebounceFn((value) => {
  searchQuery.value = value;
}, 300);
</script>

<template>
  <input type="text" @input="(e) => updateSearch(e.target.value)" placeholder="Search..." />
  <ul>
    <li v-for="item in filteredItems" :key="item">{{ item }}</li>
  </ul>
</template>
Enter fullscreen mode Exit fullscreen mode

This reduces the number of filtering operations while typing.

5. Paginate Large Lists Instead of Rendering All at Once

Instead of displaying all items at once, break them into pages.

<script setup>
import { ref, computed } from 'vue';

const items = ref(Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`));
const currentPage = ref(1);
const itemsPerPage = 20;

const paginatedItems = computed(() => {
  const start = (currentPage.value - 1) * itemsPerPage;
  return items.value.slice(start, start + itemsPerPage);
});
</script>

<template>
  <ul>
    <li v-for="item in paginatedItems" :key="item">{{ item }}</li>
  </ul>
  <button @click="currentPage--" :disabled="currentPage === 1">Previous</button>
  <button @click="currentPage++" :disabled="currentPage * itemsPerPage >= items.length">Next</button>
</template>
Enter fullscreen mode Exit fullscreen mode

Pagination improves performance by displaying only a subset of the data at a time.

📖 Learn more

If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below:

Vue School Link

It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉

✅ Summary

Efficiently handling large lists in Vue 3 requires a combination of all the techniques mentioned above. By implementing them, you can ensure your Vue 3 applications remain fast and responsive, even with massive datasets.

Take care and see you next time!

And happy coding as always 🖥️

Image of DataStax

Langflow: Simplify AI Agent Building

Connect models, vector stores, memory and other AI building blocks with the click of a button to build and deploy AI-powered agents.

Get started for free

Top comments (4)

Collapse
 
pengeszikra profile image
Peter Vivo

This method is total work in any FW or no-FW, nice job.

Collapse
 
aloisseckar profile image
Alois Sečkár

lazy-loading can also be easilly achieved with vueuse.org/core/useInfiniteScroll

it works like in social media feeds - you just keep scrolling down the page and the new results keep appearing - but ofc it is not suitable for all purposes - e.g. if you actually need to be able to display certain "page" - then your pagination solution is indeed better

Collapse
 
avelino_nogueira_2f1e3513 profile image
Avelino Nogueira

Is there any performance issues with
<li v-for="(item, index) in items" :key="somelist-${index}">?

Collapse
 
jacobandrewsky profile image
Jakub Andrzejewski

Haven't benchmarked that but I suppose it will work better than just the plain index. But if you can, I would recommend to use an id that is fully unique rather than 0,1,2,3,4 :)

Image of DataStax

AI Agents Made Easy with Langflow

Connect models, vector stores, memory and other AI building blocks with the click of a button to build and deploy AI-powered agents.

Get started for free

👋 There's an app for that
Billboard image

Please stick around with the Forem app — the best way to keep up with DEV and other tech communities.

AI is changing our lives. Navigate the change with community!

Okay