createVirtual
Virtual scrolling composable for efficiently rendering large lists by only rendering visible items.
Usage
The createVirtual composable efficiently renders large lists by only mounting visible items plus a small overscan buffer. Pass an array of items and configure the item height to get back sliced items, scroll handlers, and positioning values.
0 (rendered) / 10000
<script setup lang="ts">
import { createVirtual } from '@vuetify/v0'
import { computed, shallowRef } from 'vue'
const items = shallowRef(
Array.from({ length: 10_000 }, (_, i) => ({
id: i,
name: `Item ${i + 1}`,
value: Math.floor(Math.random() * 1000),
})),
)
const virtual = createVirtual(items, { itemHeight: 40 })
const {
element,
items: virtualItems,
offset,
size,
scroll,
scrollTo,
} = virtual
const stats = computed(() => ({
total: items.value.length,
rendered: virtualItems.value.length,
}))
const jumpTo = shallowRef('')
function handleJumpTo () {
const index = Number.parseInt(jumpTo.value) - 1
if (index < 0 || index > items.value.length) return
scrollTo(index)
}
function addItems () {
const newItems = Array.from({ length: 100 }, (_, i) => ({
id: items.value.length + i,
name: `Item ${items.value.length + i + 1}`,
value: Math.floor(Math.random() * 1000),
}))
items.value = [...items.value, ...newItems]
}
</script>
<template>
<div class="flex flex-col gap-3">
<div class="flex gap-2 items-center text-sm flex-wrap">
<input
v-model="jumpTo"
class="px-2 py-1 border border-divider bg-surface text-on-surface rounded w-24 flex-1 md:flex-none"
placeholder="Jump to..."
type="number"
@keyup.enter="handleJumpTo"
>
<button class="px-3 py-1 border border-divider rounded hover:bg-surface-tint" @click="handleJumpTo">
Jump
</button>
<button class="px-3 py-1 border border-divider rounded hover:bg-surface-tint" @click="addItems">
Add 100
</button>
<span class="text-on-surface opacity-60 ml-auto">
{{ stats.rendered }} (rendered) / {{ stats.total }}
</span>
</div>
<div
ref="element"
class="h-[300px] overflow-y-auto border border-divider rounded"
@scroll="scroll"
>
<div :style="{ height: `${offset}px` }" />
<div
v-for="item in virtualItems"
:key="item.index"
class="h-[40px] px-4 flex items-center justify-between border-b border-divider hover:bg-surface-tint"
>
<span class="font-mono text-sm text-on-surface">{{ item.raw.name }}</span>
<span class="text-on-surface opacity-60">{{ item.raw.value }}</span>
</div>
<div :style="{ height: `${size}px` }" />
</div>
</div>
</template>
Architecture
The rendering pipeline transforms scroll events into visible item ranges:
API Pattern
| Function | Purpose |
|---|---|
createVirtual(items, options) | Factory - returns a virtual scrolling context |
createVirtualContext(items, options) | Factory with DI - returns [useVirtual, provideVirtual, virtual] trinity |
useVirtual(namespace?) | Injection getter - retrieves provided virtual context |
Basic Usage
ts
import { ref } from 'vue'
import { createVirtual } from '@vuetify/v0'
const items = ref(Array.from({ length: 10000 }, (_, i) => ({ id: i })))
const virtual = createVirtual(items, { itemHeight: 40 })With Dependency Injection
ts
// Create context with DI support
const [useVirtual, provideVirtual, virtual] = createVirtualContext(items, {
namespace: 'app:virtual',
overscan: 10,
})
// In parent component
provideVirtual()
// In child component
const virtual = useVirtual() The following API details are for the createVirtual composable.
Functions
createVirtual
(items: Ref<readonly T[], readonly T[]>, _options?: VirtualOptions) => VirtualContext<T>Virtual scrolling composable for efficiently rendering large lists
createVirtualContext
(items: Ref<readonly T[], readonly T[]>, _options?: VirtualContextOptions) => ContextTrinity<VirtualContext<T>>Creates a virtual scrolling context with dependency injection support.
useVirtual
(namespace?: string) => VirtualContext<T>Returns the current virtual context from dependency injection.
Options
onStartReached
(distance: number) => void | Promise<void>The callback to call when the start is reached.
Properties
Methods
Was this page helpful?