API Reference
Complete documentation for @pdanpdan/virtual-scroll.
Introduction
@pdanpdan/virtual-scroll is a high-performance Vue 3 virtual scroll library designed to handle massive lists with ease. It supports vertical, horizontal, and bidirectional (grid) scrolling, dynamic item sizes using ResizeObserver, and full support for Right-to-Left (RTL) layouts.
Performance
Virtualization keeps the DOM small by rendering only the items in the viewport (plus a configurable buffer), so scrolling stays responsive regardless of dataset size. Scroll handling and range calculations are optimized for every sizing mode; the biggest wins come from the configuration choices below.
Scroll performance & scrollbar dragging
Browsers throttle native scrollbar dragging: while the thumb is dragged, the scroll position advances in coarse per-frame steps. As a result, the content (and the virtualized items that follow it) can lag noticeably behind the thumb on large drags, even when the target area was already rendered. Wheel, touch, and keyboard scrolling are not affected.
To keep scrollbar dragging instant and 1:1 with the pointer, it is strongly suggested to use the built-in virtual scrollbars:
- Enable them with the
virtualScrollbarprop onVirtualScroll(they are also enabled automatically for lists beyond the browser size limit). - Match your design using the --vs-scrollbar-* CSS variables, or take full control of the scrollbar UI with the
scrollbarscoped slot.
The native scrollbar is hidden automatically (.virtual-scroll--hide-scrollbar) whenever virtual scrollbars are active - no extra CSS is needed.
Note: virtual scrollbars are not available when the scroll container is the window or the body - use an element container.
Other Performance Advice
- Prefer fixed sizes. A numeric
itemSize/columnWidthgives O(1) range math; arrays and functions use O(log n) Fenwick-tree lookups; dynamic (measured) sizes are the most expensive and re-measure withResizeObserver. See the Sizing Guide below. - Skip per-row data for uniform lists. A numeric
itemSize/columnWidthallocates no per-row storage and positions rows arithmetically, so index-only lists (sparseitems, e.g.new Array(10_000_000)) keep memory flat at any scale - render row content from the slot'sindexinstead ofitem. - Keep buffers modest.
bufferBefore/bufferAfter(default 5) trade rendering cost for scrolling smoothness - a larger buffer means more DOM nodes and more work per frame. - Keep item content cheap. The item slot is re-rendered on scroll; avoid heavy markup, images, or effects inside items.
- Use an element container for scrollable UIs instead of the window or body: it isolates scrolling, enables virtual scrollbars, and avoids full-page layout work.
- Use
ssrRangeto pre-render the initial viewport and skip the first measure/scroll pass on slow devices. - Massive lists are handled automatically. Beyond the browser's ~10,000,000 px limit the library switches to coordinate scaling - virtual content units (VU) are mapped onto the browser's display units (DU) - so no extra configuration is needed, except with window/body containers, where coordinate scaling and virtual scrollbars are disabled.
Sizing Guide
The library offers flexible ways to define item and column sizes. Calculations are optimized based on the type of sizing used.
| Type | itemSize / columnWidth | Perf | Description |
|---|---|---|---|
| Fixed | number | Best | Uniform size for all items. Calculations are O(1). |
| Array (Circular Pattern) | number[] | Great | Repeating size patterns from array (e.g. [50, 100]). O(log n). |
| Function | (item, idx) => number | Good | Known but variable sizes. No ResizeObserver overhead unless measured size differs. |
| Dynamic | 0, null, undefined | Fair | Sizes measured via ResizeObserver after rendering. |
Key Features
Bidirectional Scrolling
Virtualize both rows and columns for massive data grids.
Dynamic Item Sizes
Automatic measurement via ResizeObserver for precise scrolling.
RTL Support
Automatic direction detection and correct coordinate mapping for RTL layouts.
Native Window Scroll
Use the browser window/body as the scroll container.
Sticky Headers/Footers
iOS-style pushing headers for segmented lists and groups.
Scroll Restoration
Maintains position when prepending items (perfect for chat).
SSR & Hydration
Full support for server-side rendering and client hydration.
Massive List Support
Handles 10M+ items via automatic coordinate scaling (except for window/body containers).
Virtual Scrollbars
Fully customizable virtual scrollbars that replace native ones.
Scroll Snapping
Auto-align items to viewport edges or center (start, center, end, auto).
Circular Sizing Patterns
Pass arrays to define repeating size patterns for items or columns.
Masonry Layout
Real masonry in one scroll container: responsive columns, canonical oracle heights, anchored reflow, bounded DOM at any scale.
Quick Start
Install the package using your favorite package manager:
pnpm add @pdanpdan/virtual-scrollBasic usage in a Vue component:
<script setup>
import { VirtualScroll } from "@pdanpdan/virtual-scroll";
import "@pdanpdan/virtual-scroll/style.css";
const items = Array.from({ length: 1000 }, (_, i) => ({ id: i, name: `Item ${i}` }));
</script>
<template>
<VirtualScroll :items="items" :item-size="50" class="h-96">
<template #item="{ item }">
<div class="h-12 flex items-center px-4 border-b border-base-200">
{{ item.name }}
</div>
</template>
</VirtualScroll>
</template>Usage Modes
Compiled Component
Recommended for most projects. Uses pre-compiled JavaScript.
import { VirtualScroll } from "@pdanpdan/virtual-scroll";
import "@pdanpdan/virtual-scroll/style.css";- Compatible with all modern bundlers.
- Note: Manual CSS import is required.
Original Vue SFC
Import raw source for custom compilation.
import VS from "@pdanpdan/virtual-scroll/VirtualScroll.vue";- Enables better tree-shaking in your build.
- Styles handled by your Vue loader.
CDN Usage
Use directly in the browser without a build step.
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pdanpdan/virtual-scroll/dist/virtual-scroll.css">
<script src="https://cdn.jsdelivr.net/npm/@pdanpdan/virtual-scroll/dist/index.js"></script>- No installation required.
- Available via
window.VirtualScroll.
CDN Stand-alone Examples
Full-page HTML examples loading all dependencies from CDN.
Extensions
The library uses a highly modular architecture powered by extensions. Extensions can tap into the core lifecycle to add features like RTL support, snapping, or custom loading logic without bloating the core engine.
Built-in Extensions
useRtlExtension(): Automatic RTL support.useSnappingExtension(): Scroll snapping.useStickyExtension(): Sticky elements.useInfiniteLoadingExtension(): Data loading.usePrependRestorationExtension(): Position maintenance.useCoordinateScalingExtension(): Massive lists.
Usage with Composable
When using the low-level useVirtualScroll composable:
import {
useVirtualScroll,
useRtlExtension,
useSnappingExtension
} from '@pdanpdan/virtual-scroll';
const vs = useVirtualScroll(props, [
useRtlExtension(),
useSnappingExtension()
]);VirtualScroll Component
The VirtualScroll component is the primary way to use this library. It provides a declarative Vue interface for virtualizing large lists and grids, handling all rendering, recycling, and scroll logic automatically.
Props
Core Configuration
| Prop | Type | Default | Description |
|---|---|---|---|
items | T[] | - | The array of items to render. Required. Entries may be undefined (e.g. new Array(n) for index-only lists): every index in range renders and the slot item is undefined for holes; only the visible window is ever accessed. |
itemSize | num | arr | fn | null | 40 | Fixed size, circular array pattern, or function. See the Sizing Guide. |
direction | 'vertical' | 'horizontal' | 'both' | 'vertical' | The scroll direction. |
gap | number | 0 | Spacing between items (vertical or horizontal). |
Grid Configuration (only for direction="both")
| Prop | Type | Default | Description |
|---|---|---|---|
columnCount | number | 0 | Number of columns for grid mode. |
columnWidth | num | arr | fn | null | 100 | Width for columns in grid mode (supports fixed, array pattern, or function). |
columnGap | number | 0 | Spacing between columns. |
Features & Behavior
| Prop | Type | Default | Description |
|---|---|---|---|
stickyIndices | number[] | [] | Indices of items that should remain sticky. When stickyHeader or stickyFooter are enabled, they stick below or above them. |
stickyHeader / stickyFooter | boolean | false | If true, the header or footer size is measured and added to padding. Sticky stickyIndices items align below or above them. |
ssrRange | {start, end, ...} | - | Range of items to pre-render. See SSR Support. |
loading | boolean | false | While true, reveals the #loading slot (the slot stays mounted when provided and is hidden via CSS while false) and suppresses repeated load events. |
loadDistance | number | 200 | Distance from the end to trigger the load event. |
snap | bool | SnapMode | false | Automatically align to nearest item after scroll stops. See SnapMode. |
virtualScrollbar | boolean | false | Whether to force the use of virtual scrollbars. Automatically enabled for massive lists. Note: Disabled when using window or body as the container. |
restoreScrollOnPrepend | boolean | false | Maintain scroll position when items are added to the top. |
container | HTMLElement | Window | hostRef | The scrollable container. Defaults to the component's root element. Pass window or document.body to scroll the page. |
initialScrollIndex | number | - | Index to jump to on mount. |
initialScrollAlign | ScrollAlignment | Options | 'start' | Alignment for initial index. |
Accessibility
| Prop | Type | Default | Description |
|---|---|---|---|
role | string | 'list' | 'grid' | ARIA role for the container. Automatically detected based on direction. |
ariaLabel | string | - | Accessible label for the scroll container. |
ariaLabelledby | string | - | ID of the element that labels the scroll container. |
itemRole | string | - | ARIA role for each item. Set to 'none' to manually apply roles using getItemAriaProps. |
ScrollAlignment
Controls the item's final position in the viewport: 'start' | 'center' | 'end' | 'auto'.
Advanced & Performance
| Prop | Type | Default | Description |
|---|---|---|---|
containerTag | string | 'div' | HTML tag for the scroll container, e.g. for semantic list markup. For tabular data use the VirtualScrollTable component. |
wrapperTag | string | 'div' | HTML tag for the items wrapper. Combine 'ul'/'ol' with itemTag: 'li' for semantic lists. Tables should use the VirtualScrollTable component. |
itemTag | string | 'div' | HTML tag for each virtualized item (e.g. 'li'). For table rows use VirtualScrollTable. |
headerTag | string | 'div' | HTML tag for the header slot wrapper (e.g. 'header'). Tables use VirtualScrollTable, whose header slot renders the <thead>. |
footerTag | string | 'div' | HTML tag for the footer slot wrapper (e.g. 'footer'). Tables use VirtualScrollTable, whose footer slot renders the <tfoot>. |
scrollPaddingStart / End | num | {x, y} | 0 | Additional padding for scroll offsets. |
bufferBefore / bufferAfter | number | 5 | Number of items to render outside the viewport. |
defaultItemSize | number | 40 | Estimated size for items before measurement. |
defaultColumnWidth | number | 100 | Estimated width for columns before measurement. |
debug | boolean | false | Enables debug mode (visible offsets and indices). |
Accessibility (ARIA)
The component automatically manages ARIA roles and attributes to ensure screen readers can navigate virtualized content. Common roles like tree, listbox, and menu are also supported.
| Role Prop | Default Item Role | Behavior |
|---|---|---|
list (default) | listitem | Standard 1D list. |
grid | row | 2D data grid or table. |
tree | treeitem | Hierarchical structure. |
listbox | option | Selectable list. |
menu | menuitem | Navigation menu. |
Slots
#item
Scoped slot for individual items.
item: T: The data item from the source array (undefinedfor holes in sparse or index-only datasets).index: number: The original 0-based index of the item.isSticky: boolean:trueif the item is configured to be sticky viastickyIndices.isStickyActive: boolean:trueif the item is currently stuck at the threshold.isStickyActiveX / Y: boolean:trueif the item is stuck at the horizontal or vertical threshold.offset: { x, y }: Calculated physical position in display units (DU).columnRange: ColumnRange: Precise indices and paddings for visible columns.getColumnWidth: (index: number) => number: Helper to get the calculated width of any column.getItemAriaProps: (index: number) => object: Helper to get ARIA attributes for an item (e.g.role="listitem",aria-posinset).getCellAriaProps: (index: number) => object: Helper to get ARIA attributes for a cell (e.g.role="gridcell",aria-colindex).gap: number: Vertical gap between items.columnGap: number: Horizontal gap between columns.
#scrollbar
Scoped slot for custom scrollbar implementation.
axis: 'vertical' | 'horizontal': The scrollbar axis.positionPercent: number: Current scroll position (0 to 1).viewportPercent: number: Viewport as percentage of total size.thumbSizePercent: number: Calculated thumb size (0 to 100).thumbPositionPercent: number: Calculated thumb position (0 to 100).trackProps: object: Attributes and listeners for the track element.thumbProps: object: Attributes and listeners for the thumb element.isDragging: boolean: Whether the thumb is currently being dragged.scrollbarProps: object: Grouped properties forVirtualScrollbar.axis: 'vertical' | 'horizontal'totalSize: numberposition: numberviewportSize: numberscrollToOffset: (offset: number) => voidcontainerId: stringisRtl: booleanariaLabel: string
#header / #footer
Content rendered above or below the virtualized items. Can be made sticky using the stickyHeader / stickyFooter props.
#loading
Always rendered when provided - hidden via the virtual-scroll-loading--hidden class (visibility: hidden) while loading is false - so it reserves its space and the End key can include its size in the scroll target. While loading is true, further load events are suppressed. Only provide the slot while a load is expected: once there is no more data, stop passing it (e.g. v-if="hasMore" on <template #loading>) so the reserved space disappears.
ScrollbarSlotProps
Properties passed to the 'scrollbar' scoped slot.
<template>
<VirtualScroll :items="items" direction="both" virtual-scrollbar>
<template #scrollbar="{ trackProps, thumbProps, axis }">
<!-- Vertical Track -->
<div v-if="axis === 'vertical'" v-bind="trackProps" class="w-2 bg-base-300">
<div v-bind="thumbProps" class="bg-primary rounded" />
</div>
<!-- Horizontal Track -->
<div v-else v-bind="trackProps" class="h-2 bg-base-300">
<div v-bind="thumbProps" class="bg-secondary rounded" />
</div>
</template>
</VirtualScroll>
</template>| Property | Type | Description |
|---|---|---|
axis | 'vertical' | 'horizontal' | The scrollbar axis. |
positionPercent | number | Scroll position percentage (0-1). |
viewportPercent | number | Viewport percentage of total (0-1). |
thumbSizePercent | number | Calculated thumb size percentage (0-100). |
thumbPositionPercent | number | Calculated thumb position percentage (0-100). |
trackProps | Record<string, unknown> | Attributes/listeners for the track. Bind with v-bind="trackProps". Includes class and style. |
thumbProps | Record<string, unknown> | Attributes/listeners for the thumb. Bind with v-bind="thumbProps". Includes class and style. |
scrollbarProps | VirtualScrollbarProps | Grouped props for the VirtualScrollbar component: axis, totalSize, position, viewportSize, scrollToOffset, containerId, isRtl, ariaLabel. Useful for <VirtualScrollbar v-bind="scrollbarProps" />. |
isDragging | boolean | Whether the thumb is currently being dragged. |
Events
| Event | Payload | Description |
|---|---|---|
scroll | ScrollDetails<T> | Emitted on every scroll position change. |
load | 'vertical' | 'horizontal' | Triggered when the user scrolls within loadDistance of the end. |
visibleRangeChange | { start, end, colStart, colEnd } | Emitted when the set of rendered indices changes. |
Keyboard Navigation
The container is keyboard-accessible when focused (tabindex="0"). It supports standard navigation keys:
#loading slot is present.snap mode).snap mode).CSS Classes
| Class | Description |
|---|---|
.virtual-scroll-container | The root scrollable container element. |
.virtual-scroll-wrapper | Wraps rendered items and provides total scrollable dimensions. |
.virtual-scroll-item | Applied to each individual rendered item. Use for general item styling. |
.virtual-scroll-header / .virtual-scroll-footer | Containers for header and footer slots. |
.virtual-scroll-loading | Container for the loading slot. |
.virtual-scroll-loading--hidden | Applied to the loading slot while loading is false: hides it with visibility: hidden while keeping its space (the slot is always rendered when provided). |
.virtual-scroll--vertical / --horizontal / --both | Direction modifiers applied to the root container. |
.virtual-scroll--hydrated | Applied after client-side mount and hydration is complete. |
.virtual-scroll--window | Applied when scrolling via the global window object. |
.virtual-scroll--table | Applied by the VirtualScrollTable component. |
.virtual-scroll--sticky | Applied to items that are currently stuck to the viewport edge. |
.virtual-scroll--debug | Visible when debug prop is active. |
.virtual-scroll--hide-scrollbar | Applied when virtual scrollbars are enabled or content is massive. |
CSS Variables
The default VirtualScrollbar can be styled using the following CSS variables:
| Variable | Default (Light/Dark) | Description |
|---|---|---|
--vs-scrollbar-bg | rgba(230,230,230,0.9) / rgba(30,30,30,0.9) | Track background color. |
--vs-scrollbar-thumb-bg | rgba(0,0,0,0.3) / rgba(255,255,255,0.3) | Thumb background color. |
--vs-scrollbar-thumb-hover-bg | rgba(0,0,0,0.6) / rgba(255,255,255,0.6) | Thumb background on hover/active. |
--vs-scrollbar-size | 8px | Width (vertical) or height (horizontal) of the scrollbar. |
--vs-scrollbar-radius | 4px | Border radius for track and thumb. |
--vs-scrollbar-cross-gap | var(--vs-scrollbar-size) | Size of gap to use where scrollbars meet. |
--vs-scrollbar-has-cross-gap | 0 | If gap should be shown where scrollbars meet. |
Exposed Members
The VirtualScroll component exposes several reactive properties and methods from the underlying logic. You can access these via a template ref.
Properties
All PropsAll component props are available on the instance.
scrollDetailsFull reactive state of the virtualizer.
columnRangeVisible column indices and paddings.
isHydratedMounted and ready for virtualization.
isRtlRight-to-Left mode active.
scrollbarPropsVerticalReactive vertical scrollbar properties.
scrollbarPropsHorizontalReactive horizontal scrollbar properties.
scaleX / scaleYCurrent coordinate scaling factors.
componentOffsetAbsolute offset of the component within its container.
renderedWidth / renderedHeightPhysical dimensions in DOM (clamped).
wrapperRole / cellRoleThe ARIA roles currently applied to the wrapper and its cells.
Methods
scrollToIndex()Scroll to a specific row/column.
scrollToOffset()Scroll to precise pixel position.
stopProgrammaticScroll()Halt smooth scroll animations.
getColumnWidth()Get calculated width of a column.
getRowHeight()Get calculated height of a row.
getRowOffset()Get virtual offset of a row.
getColumnOffset()Get virtual offset of a column.
getItemOffset()Get virtual offset of an item.
getItemSize()Get item size along scroll axis.
getRowIndexAt()Get row index at virtual offset.
getColIndexAt()Get column index at virtual offset.
getItemAriaProps()Get ARIA attributes for an item.
getCellAriaProps()Get ARIA attributes for a cell.
refresh()Reset all dynamic measurements.
updateDirection()Trigger RTL/LTR detection.
updateHostOffset()Recalculate container position.
updateItemSize()Manually register measurement.
updateItemSizes()Batch register measurements.
VirtualScrollTable Component
For tabular data use the dedicated VirtualScrollTable component: it renders semantic <table>/<tbody>/<tr> structure, keeps the virtual offsets with spacer rows in real table flow (flowTable) or with absolute rows (fallback), measures dynamic row heights, and exposes the header, footer and item slots. When the table is wider than its container it gets its own horizontal virtual scrollbar. See the Flow Table example and the Table example. Scroll snapping is supported in table mode: flow rows and absolute rows snap to the same row offsets as list mode.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
flowTable | boolean | false | Render rows in real table flow between spacer rows instead of absolutely positioning them. Vertical lists only; row heights may be uniform (itemSize) or dynamic (measured). Unsupported configurations fall back to absolute rows. |
autoSizeColumns | boolean | false | Pin column widths from the first rendered window via a colgroup with table-layout: fixed, so later windows never reflow the columns. Requires flowTable and equal direct-cell counts across rows. |
columnWidths | number[] | - | Explicit column widths (px) pinned via the colgroup; takes precedence over autoSizeColumns. |
stickyHeader / stickyFooter | boolean | false | Measure and reserve the header/footer slots; the row groups stick to the viewport edges. |
virtualScrollbar | boolean | false | Force virtual scrollbars; vertical and, when the table overflows horizontally, horizontal bars are rendered. |
Shared API with VirtualScroll
VirtualScrollTable is the same virtualization component with table semantics - almost everything on the VirtualScroll component still applies:
- Slots: the same
item,header,footer,loadingandscrollbarslots. Theitemslot receives the same scoped props (item, index, sticky flags, offsets, column range helpers). In table mode the slot content is rendered inside the row elements provided by the component, so items slot in<td>cells and header/footer slot in<th>/<td>cells. See Slots. - Events: identical
scroll,visibleRangeChangeandloadevents. See Events. - Exposed instance (via ref): the same methods and state -
scrollToIndex, scrollToOffset,refresh,updateItemSizes,stopProgrammaticScroll,scrollDetails,isHydrated,getItemOffset/getItemSizeand the rest - plus table constants (isTable: true,itemTag: 'tr',containerTag: 'table',wrapperTag: 'tbody'). See Methods. - Props: the shared base surface applies unchanged -
items,itemSize(uniform or dynamic),bufferBefore/bufferAfter,initialScrollIndex/initialScrollAlign,restoreScrollOnPrepend,infiniteScroll-related props (loadDistance,loading),ssrRange,debug,role/ARIA props andrtl. Tag customization (containerTag/wrapperTag/itemTag) lives onVirtualScrollfor semantic lists (e.g.ul/ol > li). OnVirtualScrollTablethe container, wrapper and row elements are fixed to their semantic table tags - use this component for tabular data. Grid/gap/sticky-index/scroll-padding props are not part of the table flow surface (they fall back to absolute rows), anddirectionis vertical. See Props. - Behavior & theming: the same engine wiring - keyboard navigation, coordinate scaling, custom scrollbar slot support, sticky measurements, prepend restoration - and the same CSS classes and CSS variables. The snap caveat above applies.
VirtualScrollMasonry Component
VirtualScrollMasonry renders a real masonry grid inside a single native scroll container: the column count and a fractional column width are derived from the container width, cards are placed greedily on the shortest column through segment-snapshotted column frontiers, and only the window around the scroll position is mounted (plus one segment of overscan) - the DOM stays bounded no matter the dataset size or how far the user jumps.
Heights come from the deterministic itemHeight oracle by default (canonical layout: far scrollToIndex calls land on the exact greedy position without ever mounting the path, unvisited segments are priced arithmetically, and the total is exact once the frontier chain reaches the end). With measuredHeights, mounted cards are measured instead and the measured boxes drive the layout (local determinism: reproducible per measurement history). Container reflows (resize, column-geometry changes, dataset replacement) re-anchor the topmost visible card at its screen offset instead of holding a raw pixel position. See the Masonry example.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | T[] | Required | Array of data items to virtualize. May be sparse (new Array(n)): holes render and the slot item is undefined for them; only the rendered window is accessed. |
itemHeight | fn(item, index, columnWidth) | Required | Canonical height oracle in px. MUST be deterministic - the same (index, columnWidth) must always return the same height - because placements are committed to a frontier chain and replayed from stored snapshots. Non-finite results fall back to 40; finite non-positive results clamp to 1. |
targetColumnWidth | number | 240 | Desired column width in px. The column count is derived from the container width so columns land as close as possible to this target; the actual width is fractional so the gutters divide the width exactly. |
minColumns / maxColumns | number | 1 / 10 | Column count bounds for the responsive reflow. |
measuredHeights | boolean | false | Measure mounted cards with a ResizeObserver and drive the layout from the measured boxes instead of the oracle. Off: canonical oracle layout, nothing is measured. On: cards size to their content (the oracle height becomes the pre-measure minimum, so estimate-sized first mounts do not re-flow) and every accepted measurement re-lays-out with the topmost visible card re-anchored. |
gap | number | 10 | Spacing between cards in px, applied both between columns and between rows of the layout. |
segmentSize | number | 500 | Items per layout segment - the cadence at which the real column frontier is snapshotted. Larger segments store less frontier state but make each layout step cross more items. |
virtualScrollbar | boolean | true | Render the overlay virtual scrollbar (the native one is hidden while enabled). Hidden automatically when the content fits the viewport. |
role / itemRole | string | 'list' / 'listitem' | ARIA roles for the cards wrapper and each card (grid/tree/listbox/menu wrappers map to their child roles). Set itemRole: 'none' to disable role assignment. |
ariaLabel / ariaLabelledby | string | - | Accessible label for the scroll container (the container role becomes region when labelled). |
debug | boolean | false | Outline rendered card bounds and overlay a geometry badge (#index (x, y)) per card. |
Item Slot
| Prop | Type | Description |
|---|---|---|
item / index | T | undefined / number | The original data item and its 0-based dataset index (undefined for sparse holes). |
column | number | The 0-based column the card was placed into. |
x / y | number | Card offset in px relative to the cards wrapper (the component positions the card itself via translate). |
width / height | number | Card size in px - the resolved column width and the layout-resolved height (oracle height in canonical mode; measured height with measuredHeights). In canonical mode render content to exactly fill the oracle height; with measuredHeights cards size to their content. |
Exposed Members & Events
| Member | Type | Description |
|---|---|---|
scroll (event) | MasonryScrollDetails<T> | Emitted on scroll and every layout change: rendered items (card geometry), currentIndex/currentEndIndex, range, scrollOffset/displayScrollOffset (y), viewportSize, totalSize, columnRange and scrolling flags. |
scrollDetails | MasonryScrollDetails<T> | Current scroll state (same shape as the scroll event payload). |
columns / columnWidth | number | Live resolved column count and column width in px (0 until the container is measured) - e.g. for srcset candidates or text budgets. |
totalHeight / totalHeightExact | number / boolean | Content height in px - extrapolated from the known frontier prefix until the chain reaches the end, then exact. End-anchored scrolls re-clamp as estimates settle. |
scrollToIndex | fn(index?, options?) | Scroll to a card with align ('start' | 'center' | 'end' | 'auto'), behavior and dryRun; far jumps land on the exact canonical position. |
scrollToOffset | fn(offset?, options?) | Scroll to a pixel offset (use ±Infinity for the very end/start; end intents follow the content as totals settle). |
refresh | fn() | Drop every cached frontier and re-layout from the current anchor - after in-place item edits or oracle changes. |
Sizing contract & limitations
- In canonical mode cards must render at exactly the oracle height - reserve media space (
aspect-ratio, fixed model heights) and never rely on DOM measurement. WithmeasuredHeights, cards size to their content and only mounted cards are measured (unmounted regions keep the oracle estimate). - Vertical axis only: no RTL, horizontal, or
bothmode and no coordinate scaling - very tall datasets stay below the browser's ~10M px scroll limit. - Not available for SSR pre-rendering: content mounts after the container is measured. Extensions/snap/sticky/loading of the list engine do not apply.
VirtualScrollbar Component
The VirtualScrollbar component provides a cross-browser consistent scrollbar that can be used independently or within the VirtualScroll component. Check out the Independent Scrollbars example to see it in action without virtualization.
<script setup>
import { VirtualScrollbar } from "@pdanpdan/virtual-scroll";
import { ref } from "vue";
const scrollX = ref(0);
const scrollY = ref(0);
</script>
<template>
<div class="relative overflow-hidden h-96">
<!-- Vertical Scrollbar -->
<VirtualScrollbar
axis="vertical"
:total-size="10000"
:viewport-size="400"
:position="scrollY"
@scroll-to-offset="val => scrollY = val"
/>
<!-- Horizontal Scrollbar -->
<VirtualScrollbar
axis="horizontal"
:total-size="10000"
:viewport-size="800"
:position="scrollX"
@scroll-to-offset="val => scrollX = val"
/>
</div>
</template>Props
| Prop | Type | Default | Description |
|---|---|---|---|
axis | 'vertical' | 'horizontal' | 'vertical' | The axis of the scrollbar. Defaults to 'vertical'. |
totalSize | number | - | Total size of the scrollable content in pixels. Required. |
viewportSize | number | - | Size of the visible viewport in pixels. Required. |
position | number | - | Current scroll position in pixels. Required. |
scrollToOffset | (offset: number) => void | - | Optional callback invoked with the new offset on user interaction, right before the scrollToOffset event fires. |
containerId | string | undefined | ID of the container element for accessibility. |
isRtl | boolean | false | Whether the scrollbar is in Right-to-Left (RTL) mode. |
ariaLabel | string | - | Accessible label for the scrollbar. |
Events
| Event | Payload | Description |
|---|---|---|
scroll-to-offset | number | Emitted when the user interacts with the scrollbar to change position. |
Composables
useVirtualScroll
Provides the core virtualization logic. Recommended for advanced use cases or when building custom wrappers.
import { useVirtualScroll } from '@pdanpdan/virtual-scroll';
import { computed, ref } from 'vue';
const items = ref([...]);
const props = computed(() => ({
items: items.value,
itemSize: 50,
direction: 'vertical'
}));
const {
renderedItems,
scrollDetails,
totalHeight,
scrollToIndex
} = useVirtualScroll(props);Parameters
Accepts a MaybeRefOrGetter to a VirtualScrollProps object.
Return Value
| Member | Type | Description |
|---|---|---|
renderedItems | Ref<RenderedItem<T>[]> | List of items to render in the current buffer. |
scrollDetails | Ref<ScrollDetails<T>> | Full reactive state of the virtual scroll system. |
columnRange | Ref<ColumnRange> | Visible columns and their associated paddings. |
totalWidth / totalHeight | Ref<number> | Calculated total size of the scrollable content area (DU). |
renderedWidth / renderedHeight | Ref<number> | Total dimensions to be rendered in the DOM (clamped to browser limits, DU). |
isHydrated | Ref<boolean> | true when the component is mounted and hydrated. |
isRtl | Ref<boolean> | true if the scroll container is in Right-to-Left mode. |
scaleX / scaleY | Ref<number> | Current coordinate scaling factors (VU / DU). |
componentOffset | { x: Ref<number>, y: Ref<number> } | Absolute offset of the component in its container (DU). |
scrollbarOffset | Reactive<{ x: number; y: number }> | Inline-start/block-start padding of the scroll container (DU), used to align the virtual scrollbar overlay with the scrollport. |
| scrollToIndex | Function | Programmatic scroll to a specific index. End-anchored scrolls re-clamp while settling measurements move the real end, so a first jump to the end lands flush on dynamic lists. |
| scrollToOffset | Function | Programmatic scroll to a pixel offset. |
| stopProgrammaticScroll | Function | Cancel any active smooth scroll animation. |
| handleScrollCorrection | Function | Adjust scroll position to compensate for measurement changes. |
| refresh | Function | Resets all measurements and state. |
| updateItemSize | Function | Register a manual item measurement. |
| updateItemSizes | Function | Register multiple manual item measurements. |
| updateHostOffset | Function | Force update the container's relative position. |
| updateDirection | Function | Manually trigger direction (LTR/RTL) detection. |
| getColumnWidth | Function | Helper to get a column's width. |
| getRowHeight | Function | Helper to get a row's height. |
| getRowOffset | Function | Helper to get a row's virtual offset (VU). |
| getColumnOffset | Function | Helper to get a column's virtual offset (VU). |
| getItemOffset | Function | Helper to get an item's virtual offset (VU). |
| getItemSize | Function | Helper to get an item's size along scroll axis (VU). |
| getRowIndexAt | Function | Helper to get the row (or item) index at a vertical virtual offset (VU). |
| getColIndexAt | Function | Helper to get the column index at a horizontal virtual offset (VU). |
useVirtualScrollMasonry
Masonry virtualization driver for a single native scroll container - the engine behind VirtualScrollMasonry. Owns one MasonryLayout frontier chain, renders only the cards intersecting the viewport (plus one segment of overscan per side), derives responsive column geometry from the container width and re-anchors the topmost visible card in content space on every relayout. Headless composable users provide their scrollable element via the hostRef prop.
import { useVirtualScrollMasonry } from '@pdanpdan/virtual-scroll';
import { computed, ref } from 'vue';
const items = ref([...]);
const hostRef = ref<HTMLElement | null>(null);
const props = computed(() => ({
items: items.value,
itemHeight: (item, index, width) => item.aspect * width,
hostRef: hostRef.value
}));
const {
renderedCards,
scrollDetails,
columns,
columnWidth,
totalHeight,
scrollToIndex
} = useVirtualScrollMasonry(props); Accepts a MaybeRefOrGetter to a VirtualScrollMasonryProps object (the component prop set plus hostRef). Returns renderedCards, scrollDetails, columns, columnWidth, totalHeight, totalHeightExact, scrollToIndex, scrollToOffset, refresh and the reactive internalState (scrollY, viewport size, scrolling flags) - see the component members for the semantics of each member.
useVirtualScrollSizes
Manages the underlying sizing logic using Fenwick Trees. This composable handles prefix sum calculations, size updates, and scroll correction adjustments.
import { useVirtualScrollSizes } from '@pdanpdan/virtual-scroll';
import { computed } from 'vue';
const {
itemSizesY,
updateItemSizes,
getSizeAt
} = useVirtualScrollSizes(computed(() => ({
props: { items: [], itemSize: 50 },
isDynamicItemSize: false,
isDynamicColumnWidth: false,
defaultSize: 50,
fixedItemSize: 50,
direction: 'vertical'
})));Parameters
Accepts a MaybeRefOrGetter to a UseVirtualScrollSizesProps object.
Return Value
| Member | Type | Description |
|---|---|---|
itemSizesX / Y | FenwickTree | Prefix sum trees for item sizes. |
columnSizes | FenwickTree | Prefix sum tree for column widths. |
measuredItemsX / Y | Ref<Uint8Array> | Bitmask of measured items. |
measuredColumns | Ref<Uint8Array> | Bitmask of measured columns. |
treeUpdateFlag | Ref<number> | Reactive flag that increments when trees update. |
sizesInitialized | Ref<boolean> | True after initial sizes are calculated. |
| getItemBaseSize | Function | Helper to get item size from props. |
| getSizeAt | Function | Helper to get current size at index. |
| initializeSizes | Function | Setup trees from component props. |
| updateItemSizes | Function | Batch register measurements and trigger corrections. |
| refresh | Function | Reset all measurements and state. |
useVirtualScrollbar
Provides the logic for virtual scrollbar interactions. It handles track clicks, thumb dragging, and coordinate mapping (including RTL).
import { useVirtualScrollbar } from '@pdanpdan/virtual-scroll';
import { ref } from 'vue';
const scrollPos = ref(0);
const {
trackProps,
thumbProps,
thumbSizePercent,
thumbPositionPercent
} = useVirtualScrollbar(() => ({
axis: 'vertical',
totalSize: 10000,
viewportSize: 500,
position: scrollPos.value,
scrollToOffset: (val) => { scrollPos.value = val; }
}));Parameters
Accepts a MaybeRefOrGetter to a UseVirtualScrollbarProps object.
Return Value
| Member | Type | Description |
|---|---|---|
trackProps | ComputedRef<object> | Attributes and listeners for the track element. Includes class and style. |
thumbProps | ComputedRef<object> | Attributes and listeners for the thumb element. Includes class and style. |
viewportPercent | ComputedRef<number> | Viewport size as percentage of total size (0-1). |
positionPercent | ComputedRef<number> | Scroll position as percentage of scrollable range (0-1). |
thumbSizePercent | ComputedRef<number> | Calculated thumb size (percentage of track, 0-100). |
thumbPositionPercent | ComputedRef<number> | Calculated thumb position (percentage of track, 0-100). |
isDragging | Ref<boolean> | Whether the thumb is currently being dragged. |
useVirtualScrollInertia
Handles pointer-based scrolling, inertia animation, and mouse wheel events for cases where native scrolling is not available (e.g., massive lists or custom scrollbars).
import { useVirtualScrollInertia } from '@pdanpdan/virtual-scroll';
const {
isPointerScrolling,
handlePointerDown,
handlePointerMove,
handlePointerUp,
handleWheel
} = useVirtualScrollInertia({
useVirtualScrolling: ref(true),
scrollDetails,
scrollToOffset: (x, y) => { /* ... */ },
stopProgrammaticScroll: () => { /* ... */ }
});Parameters
Accepts an UseVirtualScrollInertiaOptions object.
Return Value
| Member | Type | Description |
|---|---|---|
isPointerScrolling | Ref<boolean> | True when user is actively dragging the content. |
| handlePointerDown / handlePointerMove / handlePointerUp | Function | Pointer event handlers to be bound to the scroll container. |
| handleWheel | Function | Wheel event handler to be bound to the scroll container. |
| stopInertia | Function | Immediately stops any active momentum animation. |
useVirtualScrollKeyboard
Provides keyboard navigation support for the virtual scroll container, allowing users to navigate using Arrows, Home, End, PageUp, and PageDown keys.
import { useVirtualScrollKeyboard } from '@pdanpdan/virtual-scroll';
const { handleKeyDown } = useVirtualScrollKeyboard({
props,
scrollDetails,
scrollToIndex: (row, col, opt) => { /* ... */ },
scrollToOffset: (x, y, opt) => { /* ... */ },
stopProgrammaticScroll: () => { /* ... */ },
getLoadingSlotSize: () => loadingEl?.offsetHeight ?? 0, // optional
// ... resolvers
});Parameters
Accepts an UseVirtualScrollKeyboardOptions object.
- scrollToOffset: Scrolls to a pixel position. For the
Endkey the composable requests extra range (endExtraX/endExtraYoptions) so the scroll clamp extends past the virtual content (the loading slot below the items). getLoadingSlotSize(optional): Height of the loading slot. When provided,Endincludes it in the target so the last item plus the slot fit in the viewport.
Return Value
| Member | Type | Description |
|---|---|---|
| handleKeyDown | Function | Keyboard event handler to be bound to the focusable scroll container. |
useVirtualScrollObservers
Manages ResizeObserver instances to support fully dynamic item and container sizes.
import { useVirtualScrollObservers } from '@pdanpdan/virtual-scroll';
const { setItemRef } = useVirtualScrollObservers({
hostRef,
wrapperRef,
headerRef,
footerRef,
itemRefs,
updateHostOffset: () => { /* ... */ },
updateItemSizes: (updates) => { /* ... */ },
// ...
});Parameters
Accepts an UseVirtualScrollObserversOptions object.
Return Value
| Member | Type | Description |
|---|---|---|
| setItemRef | Function | Callback ref to be used on rendered items to track and measure them. |
Extension Reference
useRtlExtension
Automatically detects the text direction (LTR or RTL) of the scroll container and adjusts the coordinate system accordingly. It ensures that horizontal scroll offsets and item positioning are correct in RTL mode.
import { useRtlExtension } from '@pdanpdan/virtual-scroll';Parameters
This extension does not accept any parameters.
Behavior
- Injects detection logic into the
updateDirectioncore method. - Detects direction from the container element, or falls back to the document root.
- Automatically flips horizontal item offsets in RTL mode.
useSnappingExtension
Adds scroll snapping behavior to the virtualizer. When user scrolling stops, the extension automatically aligns the viewport to the nearest item based on the snap prop configuration.
import { useSnappingExtension } from '@pdanpdan/virtual-scroll';Parameters
This extension does not accept any parameters.
Behavior
- Hooks into
onScrollEndlifecycle event. - Calculates the best snap target using
resolveSnaputility. - Uses
scrollToIndexwithbehavior: 'smooth'to perform the snap. - Automatically ignores items larger than the viewport to prevent infinite jumping.
useStickyExtension
Sticky rows and columns. VirtualScroll registers this extension automatically; the sticky behavior itself lives in the core engine, keyed on the stickyIndices, stickyHeader and stickyFooter props - the extension is a pass-through kept for the composable wiring contract.
import { useStickyExtension } from '@pdanpdan/virtual-scroll';Parameters
This extension does not accept any parameters.
Behavior
- Sticky items stick below the sticky header (and above the sticky footer): activation and the pushing effect are measured from the sticky start/end offsets (
stickyStartX/stickyStartYon StickyParams). - Supports both horizontal and vertical stickiness.
useInfiniteLoadingExtension
Simple extension to facilitate infinite scrolling. It monitors the scroll position and triggers a callback when the user reaches a specific distance from the end of the content.
import { useInfiniteLoadingExtension } from '@pdanpdan/virtual-scroll';
const ext = useInfiniteLoadingExtension({
onLoad: (axis) => {
console.log(`Load more items on ${axis} axis`);
}
});Parameters
| Property | Type | Description |
|---|---|---|
onLoad | (axis: 'vertical' | 'horizontal') => void | Callback triggered when a threshold is met. |
Behavior
- Watches
scrollDetailsreactively. - Respects the
loadDistanceandloadingprops from the component. - Prevents duplicate triggers while
loadingis true. - Fires as soon as the threshold is reached - including while a programmatic scroll (scrollbar drag, PageDown/End) is still settling - so the loading indicator appears promptly and is not skipped.
usePrependRestorationExtension
Essential for chat-like interfaces. When items are prepended to the beginning of the items array, this extension calculates the added size and applies a scroll correction to maintain the user's perceived position.
import { usePrependRestorationExtension } from '@pdanpdan/virtual-scroll';Parameters
This extension does not accept any parameters.
Behavior
- Compares new items with previous items to detect prepended count.
- Calculates the height (or width) of prepended items.
- Uses
handleScrollCorrectionto silently adjust the scroll position before the next frame.
useCoordinateScalingExtension
Enables support for virtually unlimited content sizes. Since browsers have a hard limit on the physical height/width of elements (usually around 10M to 30M pixels), this extension scales the display coordinates so the virtual list can represent billions of pixels.
import { useCoordinateScalingExtension } from '@pdanpdan/virtual-scroll';Parameters
This extension does not accept any parameters.
Behavior
- Calculates
scaleXandscaleYfactors when total size exceeds browser limits. - Transparently maps physical scroll events to virtual positions.
- Automatically disabled when using
windowas the container (as the browser handles body scrolling differently).
API Reference
Types
ScrollDirection
'vertical' | 'horizontal' | 'both'Defines the virtualization axes for the VirtualScroll component.
ScrollDetails<T>
| Property | Type | Description |
|---|---|---|
items | RenderedItem<T>[] | Rendered items in the buffer. |
currentIndex | number | First visible row index below any sticky header. |
currentColIndex | number | First visible column index after any sticky column. |
currentEndIndex | number | Index of the last item visible above any sticky footer. |
currentEndColIndex | number | Index of the last column visible before any sticky end column (grid mode). |
scrollOffset | { x, y } | Current relative scroll position in virtual units (VU). |
displayScrollOffset | { x, y } | Current physical scroll position in display pixels (DU). |
viewportSize | { width, height } | Dimensions of the visible viewport in virtual units (VU). |
displayViewportSize | { width, height } | Physical dimensions of the visible viewport in display pixels (DU). |
totalSize | { width, height } | Estimated total content dimensions (VU). |
isScrolling | boolean | Active scrolling state. |
isProgrammaticScroll | boolean | True if triggered by scrollToIndex/Offset. |
range | { start, end } | Range of currently rendered item indices, including the scroll buffer (inclusive start, exclusive end). |
columnRange | ColumnRange | Visible column range (grid). |
RenderedItem<T>
| Property | Type | Description |
|---|---|---|
item | T | The source data item. |
index | number | Item's position in the array. |
offset | { x, y } | Absolute pixel position within the wrapper (DU). |
size | { width, height } | Current dimensions (VU). |
originalX / originalY | number | Offsets before any sticky adjustments (VU). |
isSticky | boolean | Is configured as sticky. |
isStickyActive | boolean | Currently stuck to the edge. |
isStickyActiveX / isStickyActiveY | boolean | Currently stuck to the horizontal/vertical edge respectively. |
stickyOffset | { x, y } | Translation applied for sticky pushing effect (DU). |
ColumnRange
| Property | Type | Description |
|---|---|---|
start | number | Index of first rendered column. |
end | number | Index of last rendered column (exclusive). |
padStart | number | Pixel space to maintain before columns (VU). |
padEnd | number | Pixel space to maintain after columns (VU). |
VirtualScrollProps<T>
Core configuration properties shared between the component and the composables (a subset of the full prop tables above; hostElement is accepted by the composable only).
| Property | Type | Description |
|---|---|---|
items | T[] | Data source. Required. |
itemSize | num | arr | fn | null | Sizing logic (fixed, circular array pattern, or function). Default: 40px. |
direction | ScrollDirection | 'vertical' | 'horizontal' | 'both'. |
bufferBefore / bufferAfter | number | Items outside viewport. Default: 5. |
container | HTMLElement | Window | Scroll container. Defaults to component root. |
hostElement | HTMLElement | Reference for offset calculation (DU). |
ssrRange | SSRRange | Pre-rendered range for SSR. |
columnCount | number | Total columns for grid mode. |
columnWidth | num | arr | fn | null | Column sizing. Default: 100px. |
scrollPaddingStart / End | num | {x, y} | Pixel offsets for scroll limits. |
gap / columnGap | number | Pixel space between items/cols. |
restoreScrollOnPrepend | boolean | Maintain chat scroll position. |
snap | SnapMode | Auto-alignment after scroll stop. |
initialScrollIndex | number | Mount-time jump index. |
initialScrollAlign | ScrollAlignment | Options | Alignment for initial jump. |
defaultItemSize | number | Estimate for dynamic items. |
defaultColumnWidth | number | Estimate for dynamic columns. |
debug | boolean | Enable visualization. |
StickyParams
Parameters for calculating sticky item offsets (core engine's calculateStickyItem).
| Property | Type | Description |
|---|---|---|
index | number | Item index. |
isSticky | boolean | Whether the item is configured as sticky. |
direction | ScrollDirection | Scroll direction. |
relativeScrollX / relativeScrollY | number | Virtual scroll position (VU). |
originalX / originalY | number | Virtual original position of the item (VU). |
width / height | number | Virtual item size (VU). |
stickyIndices | number[] | All configured sticky indices. |
fixedSize / fixedWidth | number | null | Fixed item size / column width (VU), null for dynamic. |
gap / columnGap | number | Item / column gap (VU). |
getItemQueryY / getItemQueryX | (index: number) => number | Prefix sum resolvers for offsets (VU). |
stickyStartX / stickyStartY | number | Size of sticky start elements (left/top) in DU. Sticky items stick below them; activation and the pushing effect are measured from this offset. Optional, defaults to 0. |
UseVirtualScrollbarProps
| Property | Type | Description |
|---|---|---|
axis | ScrollAxis | Axis of the scrollbar. |
totalSize | number | Total size of content in pixels. |
position | number | Current scroll position in pixels. |
viewportSize | number | Visible area size in pixels. |
| scrollToOffset | (offset: number) => void | Callback to update position. |
containerId | string | ID for accessibility. |
isRtl | boolean | Enable RTL mapping. |
ariaLabel | string | Accessible label for the scrollbar. |
ScrollToIndexOptions
Full configuration for index-based scrolling.
| Property | Type | Description |
|---|---|---|
align | ScrollAlignment | Options | Where to align the item (default: 'auto'). |
behavior | 'auto' | 'smooth' | Scroll behavior (default: 'smooth'). |
ScrollAlignmentOptions
Allows axis-specific alignment in scrollToIndex.
| Property | Type | Description |
|---|---|---|
x | ScrollAlignment | Alignment on the horizontal axis. |
y | ScrollAlignment | Alignment on the vertical axis. |
ScrollAlignment
Controls the item's final position in the viewport during scrollToIndex.
| Value | Behavior |
|---|---|
'start' | Aligns to top (vertical) or left (horizontal) edge. |
'center' | Aligns to viewport center. |
'end' | Aligns to bottom (vertical) or right (horizontal) edge. |
'auto' Default | Smart: If the item is already fully visible, no scroll occurs. Otherwise, aligns to 'start' or 'end' to bring it into view. |
SnapMode
Defines how items align when user scrolling stops. Note: Snapping is disabled for items larger than the viewport.
| Value | Behavior |
|---|---|
false | No snapping (default). |
true / 'auto' | Smart Directional: If scrolling towards start, acts as 'end'. If scrolling towards end, acts as 'start'. |
'next' | Snaps to the next (closest) snap position in the direction of the scroll. |
'start' | Snaps the first visible item to the top/left edge if >= 50% is visible, otherwise snaps the next item. |
'center' | Snaps the item intersecting the viewport center to be exactly centered. |
'end' | Snaps the last visible item to the bottom/right edge if >= 50% is visible, otherwise snaps the previous item. |
UseVirtualScrollSizesProps
| Property | Type | Description |
|---|---|---|
props | VirtualScrollProps | Virtual scroll configuration. |
isDynamicItemSize | boolean | Whether items have dynamic heights/widths. |
isDynamicColumnWidth | boolean | Whether columns have dynamic widths. |
defaultSize | number | Fallback size for items before they are measured. |
fixedItemSize | number | null | Fixed item size if applicable. |
direction | ScrollDirection | The scroll direction. |
FenwickTree
A highly optimized data structure for O(log n) prefix sum calculations and point updates.
| Method | Signature | Description |
|---|---|---|
update | (index, delta) => void | Update value at index and propagate changes. |
query | (index) => number | Get prefix sum up to index (exclusive). |
get | (index) => number | Get individual value at index. |
set | (index, value) => void | Set the individual value at an index without updating the prefix sum tree. |
getValues | () => Readonly<Float64Array> | Get the underlying values as a read-only view (logical size). |
length | number | Logical number of items in the tree (read-only property). |
findLowerBound | (value) => number | Find largest index where prefix sum <= value. |
rebuild | () => void | Rebuild tree from current values in O(n). |
resize | (size) => void | Resize tree while preserving values. |
shift | (offset) => void | Shift values by offset (useful for prepending). |
Methods
Detailed reference for the methods exposed on the VirtualScroll component instance (via a template ref) and for the helpers returned by the composables - the badge names the owning API. Methods on the instance are also returned by useVirtualScroll.
Method scrollToIndex()
scrollToIndex(
rowIndex?: number | null,
colIndex?: number | null,
options?: ScrollAlignment | ScrollAlignmentOptions | ScrollToIndexOptions
): ScrollToIndexResultEnsures a specific item is visible within the viewport. If the item's size is dynamic and not yet measured, the scroll position will be automatically corrected after rendering. Returns the computed scroll targets in virtual and display units (ScrollToIndexResult).
| Parameter | Type | Description |
|---|---|---|
rowIndex | number | null | Target row. null to keep current Y. Optional. |
colIndex | number | null | Target column. null to keep current X. Optional. |
options | Options | Alignment and behavior settings. |
Method scrollToOffset()
scrollToOffset(
x?: number | null,
y?: number | null,
options?: { behavior?: 'auto' | 'smooth' } // behavior default: 'auto'
): voidScrolls the container to an absolute pixel position. Clamped between 0 and the calculated total size; the target is re-clamped when measurements settle (dynamic items).
Method refresh()
Invalidates all cached measurements and triggers a full re-initialization. Use this if your item source data changes in a way that affects sizes without changing the items array reference.
Method updateItemSize()
updateItemSize(
index: number,
inlineSize: number,
blockSize: number,
element?: HTMLElement
): voidManually registers a new measurement for a single item. The element parameter allows the virtualizer to detect columns from any internal structure using data-col-index attributes.
Method updateItemSizes()
updateItemSizes(updates: Array<{ index: number; inlineSize: number; blockSize: number; element?: HTMLElement }>): voidBatched version of updateItemSize. More efficient when many items are measured simultaneously.
Method updateHostOffset()
Forces a recalculation of the host element's position relative to the scroll container. Call this if the layout changes in a way that shifts the component without triggering a resize event.
Method updateDirection()
Manually triggers the detection of the scroll direction (LTR or RTL). The component also performs this automatically on mount and whenever the container prop changes.
Method getColumnWidth()
getColumnWidth(index: number): numberReturns the currently calculated width for a specific column index, taking measurements and gaps into account.
Method getRowHeight()
getRowHeight(index: number): numberReturns the currently calculated height for a specific row index, taking measurements and gaps into account.
Method getRowOffset()
getRowOffset(index: number): numberReturns the virtual vertical offset (top) of a row in virtual units (VU).
Method getColumnOffset()
getColumnOffset(index: number): numberReturns the virtual horizontal offset (left) of a column in virtual units (VU).
Method getItemOffset()
getItemOffset(index: number): numberReturns the virtual offset of an item along the scroll axis in virtual units (VU).
Method getItemSize()
getItemSize(index: number): numberReturns the size of an item along the scroll axis in virtual units (VU).
Method getRowIndexAt()
getRowIndexAt(offset: number): numberReturns the row (or item) index at a specific vertical (or horizontal in horizontal mode) virtual offset (VU).
Method getColIndexAt()
getColIndexAt(offset: number): numberReturns the column index at a specific horizontal virtual offset (VU).
Method getItemAriaProps()
getItemAriaProps(index: number): Record<string, string | number | undefined>Returns the ARIA attributes for an item at the given index. Includes role, aria-setsize, and aria-posinset (or aria-rowindex for grids).
Method getCellAriaProps()
getCellAriaProps(colIndex: number): Record<string, string | number | undefined>Returns the ARIA attributes for a cell at the given column index. Only relevant for direction="both" or role="grid". Includes role="gridcell" and aria-colindex.
Method stopProgrammaticScroll()
Immediately halts any active smooth scroll animation and clears pending scroll requests.
useVirtualScroll handleScrollCorrection()
handleScrollCorrection(addedX: number, addedY: number): voidApplies the delta accumulated by measurement changes above the viewport, keeping the visible content stable when item sizes settle.
useVirtualScrollSizes getItemBaseSize()
getItemBaseSize(item: T, index: number): numberReturns the configured base size for an item (itemSize function result or the default size) used before measurement.
useVirtualScrollSizes getSizeAt()
getSizeAt(index: number, sizeProp, defaultSize: number, gap: number, tree: FenwickTree, isX: boolean): numberQueries the size of an index from a Fenwick tree, honoring the configured size source, defaults, gaps and tree updates.
useVirtualScrollSizes initializeSizes()
initializeSizes(): voidRebuilds the size trees from the configured sizes and clears all measurement flags.
useVirtualScrollInertia handlePointerDown()
handlePointerDown(event: PointerEvent): voidStarts scaled drag/inertia handling on pointer down.
useVirtualScrollInertia handlePointerMove()
handlePointerMove(event: PointerEvent): voidTracks pointer movement while dragging (used by scaled touch/wheel inertia).
useVirtualScrollInertia handlePointerUp()
handlePointerUp(event: PointerEvent): voidEnds a drag sequence and launches inertia when needed.
useVirtualScrollInertia handleWheel()
handleWheel(event: WheelEvent): voidHandles wheel input when coordinate scaling is active so 1:1 movement is preserved.
useVirtualScrollInertia stopInertia()
stopInertia(): voidImmediately halts any running inertia animation.
useVirtualScrollKeyboard handleKeyDown()
handleKeyDown(event: KeyboardEvent): voidImplements keyboard navigation (arrows, Home/End, PageUp/PageDown) with alignment support.
useVirtualScrollObservers setItemRef()
setItemRef(el: unknown, index: number): voidCallback ref used by rendered items: registers/unregisters elements for dynamic measurement.
Utility Functions
Type Guards
isElement(val?): Checks if a value is a standard HTMLElement (explicitly excluding window). Optional.
isWindow(val?): Checks for global window object. Optional.
isBody(val?): Checks for document.body. Optional.
isWindowLike(val?): Matches window or body. Optional.
isScrollableElement(val?): Checks if a value is an HTMLElement that exposes native scroll properties like scrollLeft. Optional.
isScrollToIndexOptions(val): Type guard for ScrollToIndexOptions object.
getPaddingX / getPaddingY
(p?: number | { x?: number; y?: number } | null, direction?: ScrollDirection): number
Resolves a scroll-padding value for the target axis: a number applies along the scroll axis, an object supplies per-axis x/y.
Coordinate Mapping
displayToVirtual(displayPos, hostOffset, scale): Maps display pixels (DU) to virtual content position (VU).
virtualToDisplay(virtualPos, hostOffset, scale): Maps virtual content position (VU) to display pixels (DU).
isItemVisible
(itemPos, itemSize, scrollPos, viewSize, stickyStart?, stickyEnd?): boolean
Highly accurate visibility check (VU) used for auto-alignment and rendering ranges.
Default Values & Constants
DEFAULT_ITEM_SIZE40pxDEFAULT_COLUMN_WIDTH100pxDEFAULT_BUFFER5 itemsDEFAULT_MASONRY_TARGET_COLUMN_WIDTH240pxDEFAULT_MASONRY_MIN_COLUMNS1DEFAULT_MASONRY_MAX_COLUMNS10DEFAULT_MASONRY_GAP10pxDEFAULT_MASONRY_SEGMENT_SIZE500 itemsBROWSER_MAX_SIZE10,000,000px Values applied when props are omitted or dynamic estimates are needed. BROWSER_MAX_SIZE defines the scaling threshold.
SSR & Hydration
The library supports Server-Side Rendering via the ssrRange prop. When provided, the specified items are rendered "in-flow" on the server.
Hydration is automatic: the client renders the same in-flow items before mounting to match the server HTML, then scrolls to the pre-rendered range and switches to absolute positioning for virtualization.