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.
<script setup lang="ts">
import { createVirtual } from '@vuetify/v0'
import { shallowRef } from 'vue'
const items = shallowRef(
Array.from({ length: 10_000 }, (_, i) => `Item ${i + 1}`)
)
const { element, items: visible, offset, size, scroll, scrollTo } = createVirtual(items, {
itemHeight: 40,
})
</script>
<template>
<div ref="element" class="h-[300px] overflow-y-auto" @scroll="scroll">
<div :style="{ height: `${offset}px` }" />
<div v-for="item in visible" :key="item.index">
{{ item.raw }}
</div>
<div :style="{ height: `${size}px` }" />
</div>
</template>Context / DI
Use createVirtualContext to share a virtual scroll instance across a component tree:
import { createVirtualContext } from '@vuetify/v0'
import { shallowRef } from 'vue'
const items = shallowRef([...])
export const [useVirtual, provideVirtual, virtual] =
createVirtualContext(items, {
namespace: 'my:virtual',
itemHeight: 40,
})
// In parent component
provideVirtual()
// In child component
const { items: visible, offset, size, scroll } = useVirtual()Architecture
The rendering pipeline transforms scroll events into visible item ranges:
Reactivity
| Property/Method | Reactive | Notes |
|---|---|---|
element | Ref, assign scroll container | |
items | Computed, visible items with index | |
offset | ShallowRef, readonly (top spacer height) | |
size | ShallowRef, readonly (bottom spacer height) | |
state | ShallowRef ('loading' | 'empty' | 'error' | 'ok') | |
scroll() | Call on @scroll; schedules a visible-range update via rAF | |
scrollend() | Call on @scrollend; alias of scroll() | |
scrollTo(index, options?) | Scroll to item by index; accepts behavior, block, offset | |
resize(index, height) | Notify of a dynamic item height change; triggers offset rebuild | |
reset() | Reset state to 'ok' and restore scroll anchor |
Source items The items ref passed to createVirtual() is watched for changes. When items change, the virtual scroller updates automatically.
Examples
A 10,000-row employee directory — only the visible window is mounted to the DOM.
FAQ
offset sizes a top spacer and size sizes a bottom spacer. Together they keep the native scrollbar proportional to the full list while only the visible window of rows is actually mounted.
Configure a base itemHeight, then call resize(index, height) after each row measures itself. That notifies the scroller of the new height and triggers an offset rebuild.
createVirtual windows whatever array you pass, so filter or sort first and feed the result in — pair it with createFilter, or use createDataTable’s VirtualDataTableAdapter to hand table.items to the scroller. The source ref is watched, so the window reflows when the list changes.
Call scrollTo(index, options?) — it accepts behavior, block, and offset, so scrollTo(500, { behavior: 'smooth' }) brings item 500 into view. Bound the index with clamp so it can’t point past the data.
Pass direction: 'reverse'. The scroller pins to the bottom on mount and keeps the view anchored when new items append — the usual pattern for message threads. Pair with anchor: 'end' when you also need explicit end-anchoring across data changes.
Pass onEndReached (and/or onStartReached). Each callback receives the distance in pixels from that edge; use endThreshold / startThreshold (default 0) to fire before the user hits the absolute edge. Append to the source array inside the handler — createVirtual watches the items ref and reflows. Edge checks run from scroll(), so keep @scroll="scroll" on the container.