0
0
Vueframework~8 mins

Virtual scrolling for large lists in Vue - Performance & Optimization

Choose your learning style9 modes available
Performance: Virtual scrolling for large lists
HIGH IMPACT
Virtual scrolling improves page load speed and rendering performance by only rendering visible list items, reducing DOM size and layout calculations.
Rendering a large list of items efficiently
Vue
<template>
  <div ref="scrollContainer" style="height: 400px; overflow-y: auto;">
    <div :style="{ height: totalHeight + 'px', position: 'relative' }">
      <div
        v-for="item in visibleItems"
        :key="item.id"
        :style="{ position: 'absolute', top: itemPositions[item.id] + 'px', height: itemHeight + 'px', width: '100%' }"
      >
        {{ item.name }}
      </div>
    </div>
  </div>
</template>

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

const items = Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` }))
const itemHeight = 30
const scrollTop = ref(0)
const containerHeight = 400

const visibleCount = Math.ceil(containerHeight / itemHeight) + 1

const visibleItems = computed(() => {
  const startIndex = Math.floor(scrollTop.value / itemHeight)
  return items.slice(startIndex, startIndex + visibleCount)
})

const itemPositions = {}
items.forEach((item, index) => {
  itemPositions[item.id] = index * itemHeight
})

const totalHeight = items.length * itemHeight

const scrollContainer = ref(null)

function onScroll() {
  scrollTop.value = scrollContainer.value.scrollTop
}

onMounted(() => {
  scrollContainer.value.addEventListener('scroll', onScroll)
})

onUnmounted(() => {
  scrollContainer.value.removeEventListener('scroll', onScroll)
})
</script>
Only renders visible items plus a small buffer, drastically reducing DOM nodes and layout work.
📈 Performance GainTriggers ~15 reflows and paints instead of 10,000, improving load time and responsiveness.
Rendering a large list of items efficiently
Vue
<template>
  <ul>
    <li v-for="item in items" :key="item.id">{{ item.name }}</li>
  </ul>
</template>

<script setup>
const items = Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` }))
</script>
Rendering all 10,000 items at once creates a huge DOM tree, causing slow initial load and heavy layout calculations.
📉 Performance CostTriggers 10,000 reflows and paints, blocking rendering for hundreds of milliseconds.
Performance Comparison
PatternDOM OperationsReflowsPaint CostVerdict
Render all items10,000 nodes created10,000 reflowsHigh paint cost[X] Bad
Virtual scrolling~15 nodes created~15 reflowsLow paint cost[OK] Good
Rendering Pipeline
Virtual scrolling limits the number of DOM nodes created and updated, reducing the work in Style Calculation, Layout, and Paint stages.
Style Calculation
Layout
Paint
⚠️ BottleneckLayout stage is most expensive due to fewer elements needing size and position calculations.
Core Web Vital Affected
LCP
Virtual scrolling improves page load speed and rendering performance by only rendering visible list items, reducing DOM size and layout calculations.
Optimization Tips
1Render only visible list items to keep DOM size small.
2Use fixed item heights or calculate positions to avoid layout thrashing.
3Listen to scroll events efficiently to update visible items without blocking rendering.
Performance Quiz - 3 Questions
Test your performance knowledge
What is the main performance benefit of virtual scrolling in large lists?
ALoading all items at once to cache them
BRendering only visible items to reduce DOM size and layout work
CUsing CSS animations on all list items
DIncreasing font size for better readability
DevTools: Performance
How to check: Record a performance profile while scrolling the list. Look for long tasks and layout recalculations.
What to look for: Check for reduced layout and paint times and fewer DOM nodes in the Elements panel.