A high-performance virtualized list component for Vue 3 that efficiently renders large datasets with dynamic sizing and lazy loading
npm install @lazy-virtual-scroll/vue`` A high-performance virtualized list component for Vue 3 that efficiently renders large datasets with dynamic sizing, lazy loading, and bi-directional scrolling support. - Virtualized Rendering: Only renders items currently visible in the viewport ` The default slot is used to render each item in the list: {{ item.content }} Slot Props: The loading slot is used to render items that are still loading: Slot Props: If the Emitted when new items become visible and need to be loaded: Payload: Emitted when items go out of view: Payload: Emitted when the user scrolls: Payload: Expanded content for item {{ index }}vue
:itemSize="50"
:data="items"
:autoDetectSizes="true"
:dynamicSizes="expandedItems"
:scrollDebounce="100"
direction="column"
@load="handleLoad"
>svg" class="text-primary hover:underline" target="_blank" rel="noopener noreferrer">
Features
- Dynamic Sizing: Automatically detects and handles items of varying heights
- Lazy Loading: Load data on-demand as the user scrolls
- Bi-directional Scrolling: Support for both vertical and horizontal scrolling
- Performance Optimized: Debounced and throttled scroll handling
- Flexible Data Structure: Support for continuous or fragmented datasets
- TypeScript Support: Full type definitions includedInstallation
bashnpm
npm install @lazy-virtual-scroll/vueyarn
yarn add @lazy-virtual-scroll/vuepnpm
pnpm add @lazy-virtual-scroll/vue
``Basic Usage
vue`
:itemSize="50"
:data="items"
@load="handleLoad"
@hide="handleHide"
>
{{ item.text }}
Loading item {{ index }}...
`Slots
$3
vue`
Item {{ index }}
item
- (any): The data item from your data array or datasets. Will be undefined if data hasn't been loaded yet.index
- (number): The index of the item in the list`$3
vue`
{{ item.content }}
Loading item {{ index }}...
index
- (number): The index of the loading item#loading slot is not provided, the #default slot will be used with item as undefined.`Events
$3
vue`
startIndex
- (number): First index that needs to be loadedendIndex
- (number): Last index that needs to be loaded`$3
vue`
startIndex
- (number): First index that is now hiddenendIndex
- (number): Last index that is now hidden`$3
vue`
scrollPosition
- (number): Current scroll position in pixels`Advanced Example
vue
:itemSize="50"
:data="items"
:autoDetectSizes="true"
:dynamicSizes="expandedItems"
:scrollDebounce="100"
direction="column"
@load="handleLoad"
@hide="handleHide"
>
class="item"
:class="{'expanded': index in expandedItems}"
>
{{ item.text }}
▲
▼
v-if="index in expandedItems"
class="item-content"
:style="{
height: ${expandedItems[index]}px,${expandedItems[index]}px
minHeight:
}"
>
Loading item {{ index }}...
`
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| totalItems | number | (required) | Total number of items in the list |itemSize
| | number | (required) | Base height/width of each item in pixels |data
| | any[] | [] | Array of data items to render |datasets
| | Dataset[] | [] | Alternative to data for fragmented datasets |direction
| | 'row' \| 'column' | 'column' | Scroll direction |itemBuffer
| | number | 3 | Number of items to render outside visible area |scrollThrottle
| | number | 0 | Throttle scroll events (milliseconds) |scrollDebounce
| | number | 0 | Debounce scroll events (milliseconds) |scrollStart
| | number | 0 | Initial scroll position |dynamicSizes
| | { [itemIndex: string]: number } | {} | Manual size overrides for specific items |autoDetectSizes
| | boolean | false | Automatically detect item sizes |minItemSize
| | number | 0 | Minimum size for dynamically sized items |sortDatasets
| | boolean | true | Automatically sort datasets by startingIndex |outerMaxLengthCssValue
| | string | '100%' | Maximum length CSS value for the outer container |outerMinLengthCssValue
| | string | '100%' | Minimum length CSS value for the outer container |outerLengthCssValue
| | string | '100%' | Length CSS value for the outer container |listItemStyle
| | { [key: string]: string } | {} | Custom styles for list items |
| Event | Payload | Description |
|-------|---------|-------------|
| @load | { startIndex: number; endIndex: number } | Emitted when new items become visible and need to be loaded |@hide
| | { startIndex: number; endIndex: number } | Emitted when items go out of view and are hidden |@scroll
| | number | Emitted on scroll with current scroll position |
| Name | Props | Description |
|------|-------|-------------|
| default | { item: any, index: number } | Template for rendering each item |loading
| | { index: number } | Template for rendering loading state |
`vue
:itemSize="60"
@load="handleLoad"
@hide="handleHide"
@scroll="handleScroll"
>
Item {{ index }} {{ item ? - ${item.text} : '(Loading...)' }}
Loading item {{ index }}...
`
For scenarios where your data is loaded in chunks or comes from different sources, you can use the datasets prop instead of data:
`js`
const datasets = ref([
{ startingIndex: 0, data: [{id: 0, text: 'Item 0'}, {id: 1, text: 'Item 1'}, / ... /] },
{ startingIndex: 100, data: [{id: 100, text: 'Item 100'}, / ... /] },
// More dataset chunks...
]);
`vue`
:totalItems="10000"
:itemSize="50"
>
The component supports dynamic item sizes in two ways:
1. Manual Size Specification:
`js`
const dynamicSizes = ref({
5: 100, // Item at index 5 has height 100px
10: 200, // Item at index 10 has height 200px
});
`vue`
>
2. Automatic Size Detection:
`vue`
>
For optimal performance with large lists:
1. Use both scrollThrottle and scrollDebounce to limit scroll event processing:`
vue`
:scrollDebounce="100"
>
2. Keep component renders lightweight by using v-memo for list items:`
vue`
{{ item.text }}
The component supports dynamic item sizes in two ways:
1. Manual Size Specification:
`vue`
/>
2. Automatic Size Detection:
`vue`
/>
For optimal performance with large lists:
1. Use both scrollThrottle and scrollDebounce to limit scroll event processing:`
vue`
:scrollDebounce="100"
/>
2. Use v-memo for complex items to prevent unnecessary re-renders:`
vue`
{{ item?.content }}
3. Keep item templates simple and avoid heavy computations in templates: {{ item.description }}
`vue`
{{ item.title }}
{{ processComplexTitle(item.rawData) }}
Run nx test @lazy-virtual-scroll/vue` to execute the unit tests via Vitest.
MIT