Phoundry UI

VirtualList

Headless virtual scrolling utility. Renders only visible items for lists with thousands of rows.

import { createVirtualList } from 'phoundry-ui';

10,000 Items

Rendering 0 of 10,000 items

Show code
import { createVirtualList } from 'phoundry-ui';

const vl = createVirtualList({
  count: 10000,
  itemHeight: 32,
  overscan: 5,
});

<div use:vl.action class="h-64 overflow-auto border rounded" onscroll={vl.handleScroll}>
  <div style="height: {vl.totalHeight}px; position: relative;">
    {#each vl.items as item}
      <div
        style="position: absolute; top: {item.start}px; height: {item.size}px; width: 100%;"
        class="flex items-center px-3 text-sm border-b"
      >
        Row {item.index}
      </div>
    {/each}
  </div>
</div>

Variable row heights

Every 9th row is taller — itemHeight as a function recomputes offsets cumulatively.

Show code
const vl = createVirtualList({
  count: 800,
  itemHeight: (i) => (i % 9 === 0 ? 52 : 30),
});

<button onclick={() => vl.scrollToIndex(400, 'center')}>Jump</button>

Options

PropTypeDefaultDescription
count requirednumberTotal logical rows. Use an options object with a `get count()` getter if the backing array length changes — reads rerun whenever scroll geometry updates.
itemHeight requirednumber | (index: number) => numberFixed height or per-item height function.
overscan number5Extra items rendered above and below the viewport.

Returns

PropTypeDefaultDescription
items VirtualItem[]Visible items with index, start position, and size.
totalHeight numberTotal scrollable height in pixels.
scrollOffset numberCurrent scroll offset.
containerHeight numberMeasured container height.
handleScroll (e: Event) => voidScroll event handler to attach to the container.
scrollToIndex (index: number, align?: "start" | "center" | "end" | "auto") => voidScroll so the given index is visible; defaults to smart (“auto”) scrolling.
action ActionSvelte use:action for the scroll container.

Usage tips

  • This is a headless utility — you provide the container and item markup. Use use:vl.action on the scroll container.
  • For variable-height items, pass a function to itemHeight — e.g. (i) => i % 5 === 0 ? 48 : 32.
  • Increase overscan if you see flickering during fast scrolling.
  • Use scrollToIndex for keyboard navigation or “scroll to top/bottom” buttons.