V3 has reached EOL, Upgrade to PrimeVue 4

VirtualScroller

VirtualScroller is a performant approach to render large amounts of data efficiently.


import VirtualScroller from 'primevue/virtualscroller';

VirtualScroller requires items as the data to display, itemSize for the dimensions of an item and item template are required on component. In addition, an initial array is required based on the total number of items to display. Size of the viewport is configured using scrollWidth, scrollHeight properties directly or with CSS width and height styles.

Item #0
Item #1
Item #2
Item #3
Item #4
Item #5
Item #6
Item #7

<VirtualScroller :items="items" :itemSize="50" class="border-1 surface-border border-round" style="width: 200px; height: 200px">
    <template v-slot:item="{ item, options }">
        <div :class="['flex align-items-center p-2', { 'surface-hover': options.odd }]" style="height: 50px">{{ item }}</div>
    </template>
</VirtualScroller>

Setting orientation to horizontal enables scrolling horizontally. In this case, the itemSize should refer to the width of an item.

Item #0
Item #1
Item #2
Item #3
Item #4
Item #5
Item #6
Item #7

<VirtualScroller :items="items" :itemSize="50" orientation="horizontal" class="border-1 surface-border border-round" style="width: 200px; height: 200px" :pt="{ content: 'flex flex-row' }">
    <template v-slot:item="{ item, options }">
        <div :class="['flex align-items-center p-2', { 'surface-hover': options.odd }]" style="width: 50px; writing-mode: vertical-lr;">{{ item }}</div>
    </template>
</VirtualScroller>

Scrolling can be enabled vertically and horizontally when orientation is set as both. In this mode, itemSize should be an array where first value is the height of an item and second is the width.

Item #0_0
Item #0_1
Item #0_2
Item #0_3
Item #1_0
Item #1_1
Item #1_2
Item #1_3
Item #2_0
Item #2_1
Item #2_2
Item #2_3
Item #3_0
Item #3_1
Item #3_2
Item #3_3
Item #4_0
Item #4_1
Item #4_2
Item #4_3
Item #5_0
Item #5_1
Item #5_2
Item #5_3
Item #6_0
Item #6_1
Item #6_2
Item #6_3
Item #7_0
Item #7_1
Item #7_2
Item #7_3

<VirtualScroller :items="items" :itemSize="[50, 100]" orientation="both" class="border-1 surface-border border-round" style="width: 200px; height: 200px">
    <template v-slot:item="{ item, options }">
        <div :class="['flex align-items-center p-2', { 'surface-hover': options.odd }]" style="height: 50px">
            <template v-for="(el, index) of item" :key="index">
                <div style="width: 100px">{{ el }}</div>
            </template>
        </div>
    </template>
</VirtualScroller>

The delay property adds a threshold to wait in milliseconds during scrolling for render optimization.

No Delay
Item #0
Item #1
Item #2
Item #3
Item #4
Item #5
Item #6
Item #7
150ms
Item #0
Item #1
Item #2
Item #3
Item #4
Item #5
Item #6
Item #7
500ms
Item #0
Item #1
Item #2
Item #3
Item #4
Item #5
Item #6
Item #7

<VirtualScroller :items="items" :itemSize="50" class="border-1 surface-border border-round" style="width: 200px; height: 200px">
    <template v-slot:item="{ item, options }">
        <div :class="['flex align-items-center p-2', { 'surface-hover': options.odd }]" style="height: 50px">{{ item }}</div>
    </template>
</VirtualScroller>

<VirtualScroller :items="items" :itemSize="50" :delay="150" class="border-1 surface-border border-round" style="width: 200px; height: 200px">
    <template v-slot:item="{ item, options }">
        <div :class="['flex align-items-center p-2', { 'surface-hover': options.odd }]" style="height: 50px">{{ item }}</div>
    </template>
</VirtualScroller>

<VirtualScroller :items="items" :itemSize="50" :delay="500" class="border-1 surface-border border-round" style="width: 200px; height: 200px">
    <template v-slot:item="{ item, options }">
        <div :class="['flex align-items-center p-2', { 'surface-hover': options.odd }]" style="height: 50px">{{ item }}</div>
    </template>
</VirtualScroller>

Busy state is enabled by adding showLoader property which blocks the UI with a modal by default. Alternatively, loader template can be used to customize items e.g. with Skeleton.

Modal
Item #0
Item #1
Item #2
Item #3
Item #4
Item #5
Item #6
Item #7
Skeleton
Item #0
Item #1
Item #2
Item #3
Item #4
Item #5
Item #6
Item #7

<VirtualScroller :items="items" :itemSize="50" showLoader :delay="250" class="border-1 surface-border border-round" style="width: 200px; height: 200px">
    <template v-slot:item="{ item, options }">
        <div :class="['flex align-items-center p-2', { 'surface-hover': options.odd }]" style="height: 50px">{{ item }}</div>
    </template>
</VirtualScroller>

<VirtualScroller :items="items" :itemSize="50" showLoader :delay="250" class="border-1 surface-border border-round" style="width: 200px; height: 200px">
    <template v-slot:item="{ item, options }">
        <div :class="['flex align-items-center p-2', { 'surface-hover': options.odd }]" style="height: 50px">{{ item }}</div>
    </template>
    <template v-slot:loader="{ options }">
        <div :class="['flex align-items-center p-2', { 'surface-hover': options.odd }]" style="height: 50px">
            <Skeleton :width="options.even ? '60%' : '50%'" height="1.3rem" />
        </div>
    </template>
</VirtualScroller>

Lazy mode is handy to deal with large datasets, instead of loading the entire data, small chunks of data is loaded on demand. To implement lazy loading, enable the lazyproperty and implement onLazyLoad callback to return data.


<VirtualScroller :items="lazyItems" :itemSize="50" showLoader :delay="250" :loading="lazyLoading" lazy @lazy-load="onLazyLoad" class="border-1 surface-border border-round" style="width: 200px; height: 200px">
    <template v-slot:item="{ item, options }">
        <div :class="['flex align-items-center p-2', { 'surface-hover': options.odd }]" style="height: 50px">{{ item }}</div>
    </template>
</VirtualScroller>

Screen Reader

VirtualScroller has no specific role is enforced, still you may use any aria role and attributes as any valid attribute is passed to the container element.

Keyboard Support

Component does not include any built-in interactive elements.