VirtualList
Renders only the items inside the visible window, in fixed-height or dynamic-height mode (the latter positions via precomputed offsets and binary search).
Fixed height
Fixed
Row 0 · Item 1
Row 1 · Item 2
Row 2 · Item 3
Row 3 · Item 4
vue
<script setup lang="ts">
const listData = Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`)
</script>
<template>
<LhVirtualList :data="listData" :height="200" :item-height="40">
<template #default="{ item, index }">{{ item }}</template>
</LhVirtualList>
</template>vue
<script>
export default {
data() {
return {
listData: Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`),
}
},
}
</script>
<template>
<LhVirtualList :data="listData" :height="200" :item-height="40">
<template #default="{ item, index }">{{ item }}</template>
</LhVirtualList>
</template>Dynamic height
itemSize returns each item's height (it may be driven by real measurements).
Dynamic
Item 1
Item 2
Item 3
vue
<script setup>
const dynamicSize = (index) => (index % 3 === 0 ? 56 : 36)
</script>
<template>
<LhVirtualList :data="listData" :height="200" :item-size="dynamicSize">
<template #default="{ item, index }">{{ item }}</template>
</LhVirtualList>
</template>vue
<script>
export default {
data() {
return {
listData: Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`),
}
},
methods: {
dynamicSize(index) {
return index % 3 === 0 ? 56 : 36
},
},
}
</script>
<template>
<LhVirtualList :data="listData" :height="200" :item-size="dynamicSize">
<template #default="{ item, index }">{{ item }}</template>
</LhVirtualList>
</template>Overscan
overscan specifies how many extra rows are rendered above and below the visible window, reducing blank flashes during fast scrolling.
overscan
Row 0 · Item 1
Row 1 · Item 2
Row 2 · Item 3
Row 3 · Item 4
Row 4 · Item 5
Row 5 · Item 6
Row 6 · Item 7
Row 7 · Item 8
Row 8 · Item 9
Row 9 · Item 10
Row 10 · Item 11
Row 11 · Item 12
Row 12 · Item 13
Row 13 · Item 14
Row 14 · Item 15
Row 15 · Item 16
vue
<script setup lang="ts">
const listData = Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`)
</script>
<template>
<LhVirtualList
:data="listData"
:item-height="40"
:height="200"
:overscan="8"
/>
</template>vue
<script>
export default {
data() {
return {
listData: Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`),
}
},
}
</script>
<template>
<LhVirtualList
:data="listData"
:item-height="40"
:height="200"
:overscan="8"
/>
</template>API
LhVirtualList Props
| Attribute | Description | Type | Default |
|---|---|---|---|
| data | Data source (rendering is delegated to the default slot) | any[] | — |
| height | Container height (number is px) | string | number | — |
| itemHeight | Item height in fixed-height mode | number | — |
| itemSize | Dynamic mode: returns each item's height | (index: number) => number | — |
| overscan | Overscan item count | number | LH_VIRTUAL_OVERSCAN |
| sx | Quick layout props | LhSxProps | — |
Slot: default (scope { item, index }).
Accessibility
- The container scrolls with the keyboard; items outside the window are not in the DOM (an inherent trade-off of long lists — combine with search/filtering)
- For a full semantic grid use Table, whose virtual scrolling is likewise driven by pure core functions
Type Definitions
The TypeScript type structures exported by this component (auto-extracted from packages/components/virtual-list/src/types.ts and @lh-ui/share/core/* sources; click each entry to expand; see the Props table above for attribute descriptions):
LhVirtualListProps
ts
export interface LhVirtualListProps {
data: any[]
height: number | string
itemHeight?: number
itemSize?: (index: number) => number
overscan?: number
sx?: LhSxProps
}Constants
Public constants used by this component (linked from the Type / Default columns in the Props table above):
LH_VIRTUAL_ITEM_SIZE (from @lh-ui/share/core/virtual-list)
ts
export const LH_VIRTUAL_ITEM_SIZE = 40