VirtualGrid
A virtual grid that arranges items in rows and columns. Column count can be fixed or auto-calculated from columnWidth and the container width.
Props
items
T[]
Data array.
columns
number · default: 0
Fixed column count. Pass 0 to auto-compute from columnWidth.
columnWidth
number · default: 200
Cell width for auto-column calculation.
rowHeight
number · default: 200
Cell height in px — the initial estimate when dynamicRowHeight is on.
gap
number · default: 8
Gap between cells in px.
keyField
string · default: 'id'
Key field.
overscan
number · default: 2
Extra rows outside the viewport.
isLoading
boolean · default: false
Shows skeleton when items is empty.
motionBlur
boolean · default: false
Apply a CSS blur that scales with scroll velocity while scrolling fast.
dynamicRowHeight
boolean · default: false
Measure each row's actual height via ResizeObserver instead of a fixed rowHeight (row height = max of that row's cells).
Slots
default
Scope: { item: T, index: number, row: number, col: number }
Cell content.
empty
Scope: No scope
Shown when items is empty.
skeleton
Scope: No scope
Shown when empty and isLoading.
Emits
scroll
Payload: Event
visible-range-change
Payload: { start: number; end: number }
Exposed API
scrollTo(index, options?)
Scroll to a cell index. Unlike the other components' scrollTo, VirtualGrid derives the target row from index and doesn't take an align parameter.
getScrollElement()
Returns the element that actually scrolls — pair with VirtualScrollbar.
gridRef.value?.scrollTo(index)
gridRef.value?.scrollTo(index, { behavior: 'smooth' })
gridRef.value?.getScrollElement() // pair with VirtualScrollbarExample
<script setup lang="ts">
import { VirtualGrid } from 'vue-virtual-scroller-kit'
interface Photo {
id: number
url: string
title: string
}
const photos: Photo[] = Array.from({ length: 10_000 }, (_, i) => ({
id: i,
url: `https://picsum.photos/seed/${i}/200/200`,
title: `Photo ${i + 1}`,
}))
</script>
<template>
<VirtualGrid
:items="photos"
:column-width="220"
:row-height="220"
:gap="12"
style="height: 600px"
>
<template #default="{ item }">
<div class="photo-card">
<img :src="item.url" :alt="item.title" />
<p>{{ item.title }}</p>
</div>
</template>
</VirtualGrid>
</template>Dynamic row height — when cell content height varies (e.g. captions of different lengths), row height is measured per row instead of fixed:
<VirtualGrid
:items="photos"
:column-width="220"
:row-height="220"
dynamic-row-height
:gap="12"
style="height: 600px"
>
<template #default="{ item }">
<div class="photo-card" style="height: auto">
<img :src="item.url" :alt="item.title" />
<p>{{ item.title }}</p>
<p v-if="item.caption" class="photo-card__caption">{{ item.caption }}</p>
</div>
</template>
</VirtualGrid>With
dynamicRowHeight, cells no longer get a fixedheightfrom the grid — give themheight: auto(or leaveheightunset) so their content determines the row's real height.