Skip to content

Chapter 12: Taming the DOM: Virtualizing Long Lists

Of course. This time, we'll tackle an optimization that has nothing to do with the network or file sizes, but with the sheer number of elements you're trying to display on the page: List Virtualization.


The Scenario 📝

  • System: An application that needs to display a very long list, such as:
    • A data grid with 5,000 rows.
    • A chat history or a social media feed.
    • A long list of selectable products.
  • Problem: The developer uses a simple loop (v-for in Vue/Nuxt) to render all 5,000 items. As a result, the website becomes extremely slow, lags when scrolling, and might even crash the browser.

The Bottleneck: DOM Overload 🧐

The bottleneck here isn't the size of the data in your JavaScript; it's the number of DOM nodes the browser has to manage.

  • The Browser's Job: For each of the 5,000 items, the browser must:
    1. Create the DOM elements (e.g., <div>, <span>...).
    2. Calculate their CSS styles.
    3. Determine their layout (position, size).
    4. "Paint" them onto the screen.
  • The Core Issue: A user can only see about 20-30 items on the screen at any given time. However, you are forcing the browser to work on and manage the other 4,970 items that are currently hidden. This is a massive waste of resources.

The Solution: Use Virtualization / Windowing ✅

  • The Logic: Instead of rendering the entire list, we only render the items that are actually visible within the user's viewport.

  • How It Works:

    1. A virtualization library calculates the total height of the entire list (e.g., 5,000 items * 50px/item = 250,000px) and creates an empty container of that height to "trick" the browser's scrollbar.
    2. It only renders enough items (e.g., 20-30) to fill the screen.
    3. When the user scrolls, the library recalculates which items are about to enter the screen and which have just left.
    4. It destroys the old DOM nodes and creates new DOM nodes, using CSS transform to position them correctly within the container.

How This Happens in Nuxt/Vue ✨

Writing this logic from scratch is quite complex. Instead, the community has created excellent libraries to do the heavy lifting.

  • Popular Libraries: vue-virtual-scroller, tanstack-virtual.

You simply use the library's component instead of a standard v-for.

Example with vue-virtual-scroller:

vue
<template>
  <RecycleScroller
    class="scroller"
    :items="massiveList"
    :item-size="32" 
    v-slot="{ item }"
  >
    <div class="user">
      {{ item.name }}
    </div>
  </RecycleScroller>
</template>

<script setup>
  // massiveList is an array containing 5,000 objects
  const massiveList = [/* ... */]; 
</script>

Analyzing the Result

  1. Constant Number of DOM Nodes: Whether your list has 5,000 or 5 million items, the number of DOM nodes on the page remains low and constant (only a few dozen).
  2. Smooth Performance: The browser can easily handle this small number of elements, ensuring that scrolling and interactions remain fluid.

Conclusion:

  • The Golden Rule: "Don't render what the user can't see."
  • When you need to display a long list (a few hundred items or more), Virtualization is a mandatory technique to ensure performance.
  • Leverage well-optimized and battle-tested libraries instead of reinventing this complex logic yourself.