Skip to content
API 风格:

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 ​

AttributeDescriptionTypeDefault
dataData source (rendering is delegated to the default slot)any[]—
heightContainer height (number is px)string | number—
itemHeightItem height in fixed-height modenumber—
itemSizeDynamic mode: returns each item's height(index: number) => number—
overscanOverscan item countnumberLH_VIRTUAL_OVERSCAN
sxQuick layout propsLhSxProps—

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

Beauty of tradition, foundation of code