@glhrmoura/vue-virtualize
Vue Virtualize
Headless virtualization for Vue 3 lists, grids, and tables.
Documentation
Install
yarn add @glhrmoura/vue-virtualize
npm install @glhrmoura/vue-virtualize
Vue 3.5+ is a peer dependency.
Usage
VirtualList
<script setup lang="ts">
import { VirtualList } from '@glhrmoura/vue-virtualize'
import { shallowRef } from 'vue'
const users = shallowRef(
Array.from({ length: 100_000 }, (_, index) => ({
id: index + 1,
name: `User ${index + 1}`,
})),
)
</script>
<template>
<VirtualList :items="users" :height="500" :get-item-key="(user) => user.id">
<template #default="{ item, index }">
<UserCard :user="item" :index="index" />
</template>
</VirtualList>
</template>
estimateSize defaults to 48. A number locks a fixed item size (count × size, best for huge lists). Pass a function when rows have variable height so the list can measure mounted elements without clipping. Container scroll needs a height (unless window-scroll).
Horizontal list
<VirtualList
:items="slides"
horizontal
:width="640"
:height="180"
:estimate-size="200"
:get-item-key="(slide) => slide.id"
>
<template #default="{ item }">
<SlideCard :slide="item" />
</template>
</VirtualList>
Window scroll
Use the document/window as the scroller. No fixed container height required.
<VirtualList
:items="posts"
window-scroll
:estimate-size="72"
:get-item-key="(post) => post.id"
@visible-range-change="onRange"
@scroll-end="onScrollEnd"
>
<template #default="{ item }">
<PostCard :post="item" />
</template>
</VirtualList>
Reverse / chat feed
reverse starts at the end, sticks to the bottom when new items append, and keeps scroll stable when older items are prepended (requires stable get-item-key).
<VirtualList
:items="messages"
reverse
:height="480"
:estimate-size="56"
:get-item-key="(message) => message.id"
@visible-range-change="maybeLoadOlder"
>
<template #default="{ item }">
<MessageBubble :message="item" />
</template>
</VirtualList>
VirtualGrid
<VirtualGrid
:items="photos"
:column-count="4"
:estimate-row-size="120"
:estimate-column-size="180"
:height="500"
>
<template #default="{ item, rowIndex, columnIndex }">
<PhotoCard :photo="item" :row="rowIndex" :column="columnIndex" />
</template>
</VirtualGrid>
VirtualTable
Sticky header is on by default. Pin columns with pin: 'start' | 'end' (first column, actions, etc.). Give pinned cells a solid background so content does not show through.
const columns = [
{ id: 'name', header: 'Name', width: 220, pin: 'start' },
{ id: 'email', header: 'Email', width: 280 },
{ id: 'actions', header: '', width: 120, pin: 'end' },
]
<VirtualTable :rows="users" :columns="columns" :estimate-row-size="44" :height="500">
<template #cell="{ row, column, value }">
<span>{{ value }}</span>
</template>
</VirtualTable>
useVirtualizer
const { virtualItems, totalSize, scrollToIndex } = useVirtualizer({
count: items.length,
container,
estimateSize: () => 52,
overscan: 4,
windowScroll: false,
reverse: false,
onVisibleRangeChange: (range) => {
if (range.endIndex >= range.count - 8) loadMore()
},
onScrollEnd: () => {},
})
Events / callbacks
| Name | Where | When |
|---|---|---|
visibleRangeChange / onVisibleRangeChange |
components / composables | Visible index window changes |
scrollEnd / onScrollEnd |
components / composables | Scroll settles (~120ms idle) |
API Reference
| Export | Role |
|---|---|
VirtualList |
One axis, vertical or horizontal, optional window-scroll / reverse |
VirtualGrid |
Flat array, columnCount, row and column estimates |
VirtualTable |
rows, columns, sticky header, pinned columns |
useVirtualizer |
Custom markup driven by virtualItems and totalSize |
useVirtualList |
Headless list adapter |
useVirtualGrid |
Headless grid adapter |
useVirtualTable |
Headless table adapter |
License
Copyright (c) Guilherme Moura
Related Packages
A zero-config, fast and small virtual list, grid and masonry component for React...
Virtual scrolling for Vue 3: only the rows near the viewport are in the DOM.
Version History
| Version | Released | License |
|---|---|---|
| 0.2.2 | MIT | |
| 0.2.1 | MIT | |
| 0.2.0 | MIT | |
| 0.1.5 | MIT | |
| 0.1.4 | MIT | |
| 0.1.3 | MIT | |
| 0.1.2 | MIT | |
| 0.1.1 | MIT | |
| 0.1.0 | MIT |