useVirtualList
Create virtual lists with ease. Virtual lists (sometimes called virtual scrollers) allow you to render a large number of items performantly. They only render the minimum number of DOM nodes necessary to show the items within the container
element by using the wrapper
element to emulate the container element's full height.
Demo
Jump to index
Filter list by size
Usage
Simple list
typescript
import { useVirtualList } from '@vueuse/core'
const { list, containerProps, wrapperProps } = useVirtualList(
Array.from(Array(99999).keys()),
{
// Keep `itemHeight` in sync with the item's row.
itemHeight: 22,
},
)
import { useVirtualList } from '@vueuse/core'
const { list, containerProps, wrapperProps } = useVirtualList(
Array.from(Array(99999).keys()),
{
// Keep `itemHeight` in sync with the item's row.
itemHeight: 22,
},
)
Config
State | Type | Description |
---|---|---|
itemHeight | number | ensure that the total height of the wrapper element is calculated correctly.* |
itemWidth | number | ensure that the total width of the wrapper element is calculated correctly.* |
overscan | number | number of pre-rendered DOM nodes. Prevents whitespace between items if you scroll very quickly. |
* The itemHeight
or itemWidth
must be kept in sync with the height of each row rendered. If you are seeing extra whitespace or jitter when scrolling to the bottom of the list, ensure the itemHeight
or itemWidth
is the same height as the row.
Reactive list
typescript
import { useVirtualList, useToggle } from '@vueuse/core'
import { computed } from 'vue'
const [isEven, toggle] = useToggle()
const allItems = Array.from(Array(99999).keys())
const filteredList = computed(() => allItems.filter(i => isEven.value ? i % 2 === 0 : i % 2 === 1))
const { list, containerProps, wrapperProps } = useVirtualList(
filteredList,
{
itemHeight: 22,
},
)
import { useVirtualList, useToggle } from '@vueuse/core'
import { computed } from 'vue'
const [isEven, toggle] = useToggle()
const allItems = Array.from(Array(99999).keys())
const filteredList = computed(() => allItems.filter(i => isEven.value ? i % 2 === 0 : i % 2 === 1))
const { list, containerProps, wrapperProps } = useVirtualList(
filteredList,
{
itemHeight: 22,
},
)
html
<template>
<p>Showing {{ isEven ? 'even' : 'odd' }} items</p>
<button @click="toggle">Toggle Even/Odd</button>
<div v-bind="containerProps" style="height: 300px">
<div v-bind="wrapperProps">
<div v-for="item in list" :key="item.index" style="height: 22px">
Row: {{ item.data }}
</div>
</div>
</div>
</template>
<template>
<p>Showing {{ isEven ? 'even' : 'odd' }} items</p>
<button @click="toggle">Toggle Even/Odd</button>
<div v-bind="containerProps" style="height: 300px">
<div v-bind="wrapperProps">
<div v-for="item in list" :key="item.index" style="height: 22px">
Row: {{ item.data }}
</div>
</div>
</div>
</template>
Horizontal list
typescript
import { useVirtualList } from '@vueuse/core'
const allItems = Array.from(Array(99999).keys())
const { list, containerProps, wrapperProps } = useVirtualList(
allItems,
{
itemWidth: 200,
},
)
import { useVirtualList } from '@vueuse/core'
const allItems = Array.from(Array(99999).keys())
const { list, containerProps, wrapperProps } = useVirtualList(
allItems,
{
itemWidth: 200,
},
)
html
<template>
<div v-bind="containerProps" style="height: 300px">
<div v-bind="wrapperProps">
<div v-for="item in list" :key="item.index" style="width: 200px">
Row: {{ item.data }}
</div>
</div>
</div>
</template>
<template>
<div v-bind="containerProps" style="height: 300px">
<div v-bind="wrapperProps">
<div v-for="item in list" :key="item.index" style="width: 200px">
Row: {{ item.data }}
</div>
</div>
</div>
</template>
Component Usage
This function also provides a renderless component version via the
@vueuse/components
package. Learn more about the usage.
html
<UseVirtualList :list="list" :options="options" height="300px">
<template #="props">
<!-- you can get current item of list here -->
<div style="height: 22px">Row {{ props.data }}</div>
</template>
</UseVirtualList>
<UseVirtualList :list="list" :options="options" height="300px">
<template #="props">
<!-- you can get current item of list here -->
<div style="height: 22px">Row {{ props.data }}</div>
</template>
</UseVirtualList>
Type Declarations
Show Type Declarations
typescript
declare type UseVirtualListItemSize = number | ((index: number) => number)
export interface UseHorizontalVirtualListOptions
extends UseVirtualListOptionsBase {
/**
* item width, accept a pixel value or a function that returns the height
*
* @default 0
*/
itemWidth: UseVirtualListItemSize
}
export interface UseVerticalVirtualListOptions
extends UseVirtualListOptionsBase {
/**
* item height, accept a pixel value or a function that returns the height
*
* @default 0
*/
itemHeight: UseVirtualListItemSize
}
export interface UseVirtualListOptionsBase {
/**
* the extra buffer items outside of the view area
*
* @default 5
*/
overscan?: number
}
export declare type UseVirtualListOptions =
| UseHorizontalVirtualListOptions
| UseVerticalVirtualListOptions
export interface UseVirtualListItem<T> {
data: T
index: number
}
export interface UseVirtualListReturn<T> {
list: Ref<UseVirtualListItem<T>[]>
scrollTo: (index: number) => void
containerProps: {
ref: Ref<HTMLElement | null>
onScroll: () => void
style: StyleValue
}
wrapperProps: ComputedRef<{
style:
| {
width: string
height: string
marginTop: string
}
| {
width: string
height: string
marginLeft: string
display: string
}
}>
}
export declare function useVirtualList<T = any>(
list: MaybeRef<T[]>,
options: UseVirtualListOptions
): UseVirtualListReturn<T>
declare type UseVirtualListItemSize = number | ((index: number) => number)
export interface UseHorizontalVirtualListOptions
extends UseVirtualListOptionsBase {
/**
* item width, accept a pixel value or a function that returns the height
*
* @default 0
*/
itemWidth: UseVirtualListItemSize
}
export interface UseVerticalVirtualListOptions
extends UseVirtualListOptionsBase {
/**
* item height, accept a pixel value or a function that returns the height
*
* @default 0
*/
itemHeight: UseVirtualListItemSize
}
export interface UseVirtualListOptionsBase {
/**
* the extra buffer items outside of the view area
*
* @default 5
*/
overscan?: number
}
export declare type UseVirtualListOptions =
| UseHorizontalVirtualListOptions
| UseVerticalVirtualListOptions
export interface UseVirtualListItem<T> {
data: T
index: number
}
export interface UseVirtualListReturn<T> {
list: Ref<UseVirtualListItem<T>[]>
scrollTo: (index: number) => void
containerProps: {
ref: Ref<HTMLElement | null>
onScroll: () => void
style: StyleValue
}
wrapperProps: ComputedRef<{
style:
| {
width: string
height: string
marginTop: string
}
| {
width: string
height: string
marginLeft: string
display: string
}
}>
}
export declare function useVirtualList<T = any>(
list: MaybeRef<T[]>,
options: UseVirtualListOptions
): UseVirtualListReturn<T>
Source
Contributors
Anthony Fu
Maxim Brynze
Jelf
wheat
Jessica Sachs
Che Guevara