Skip to content

vue-virtual-scroller-kit

Virtual list, table, grid, tree, and select for Vue 3. Dynamic row heights measured by ResizeObserver, grouping with animated expand/collapse, sticky headers, infinite scroll, keyboard navigation, drag-to-reorder, RTL support, and full SSR support — all with a single peer dependency (Vue 3).

Features

  • Dynamic row heightsResizeObserver measures each row after render; position manager updates in O(log n)
  • VirtualList — flat list, external scroll container, page-mode (window scroll), DOM recycling pool, scroll restoration, opt-in horizontal layout
  • GroupedVirtualList — collapsible sections with smooth expand/collapse CSS animation, opt-in sticky group-header overlay
  • VirtualTable — native <table> with spacer-row virtual scroll, sticky header, fixed left/right columns, single and multi-column sort, drag-to-resize and drag-to-reorder columns, column show/hide, column virtualization, pinned top/bottom rows (<thead>/<tfoot>), built-in lazy loading (onLoadMore / hasMore / isLoading)
  • VirtualGrid — fixed-height cells in a responsive auto-column or fixed-column grid, or opt-in per-row dynamic height
  • VirtualTree — hierarchical expand/collapse with lazy child loading, configurable indent
  • InfiniteLoader — trigger onLoadMore near the bottom, top, or both ends; scroll-position preservation when prepending
  • VirtualSelect — virtualized dropdown with client-side or async/remote search (debounced, with a loading slot) and keyboard navigation
  • VirtualScrollbar — themable custom scrollbar overlay that syncs with any of the components above (or any scrollable element)
  • Smooth programmatic scrollingscrollTo/scrollToOffset accept { behavior: 'smooth' }, using the browser's native smooth-scroll
  • Motion blur — opt-in motionBlur prop applies a velocity-scaled CSS blur while scrolling fast, clearing once scrolling settles
  • Anchor-compensated reflow — when a row above the viewport is measured to a different height, scroll position is adjusted by the same delta so visible content never jumps
  • RTL support — CSS logical properties throughout, plus scrollLeft normalization for column virtualization, horizontal VirtualList, and the scrollbar; wrap your app in dir="rtl", nothing else required
  • useVirtualScroll — raw composable for custom containers; returns visibleRange, totalHeight, scrollTo; supports a horizontal axis
  • useVirtualKeyboardNav — arrow keys, Home/End, PageUp/PageDown, Enter/Space; plugs into any virtual list
  • useDraggableList — pointer-event drag-to-reorder with animated gap, ghost element, auto-scroll, disabled-item support
  • useRowSelection — dataset-agnostic click/Shift-click row selection (single or multi), pairs with VirtualList or VirtualTable
  • useVisibilityTracker — per-key enter/leave-viewport events backed by a real IntersectionObserver, for highlighting a nav/minimap item while its row is on screen
  • PositionManager — segment tree (O(log n) updates and prefix-sum queries) exposed for advanced use
  • autoColWidths — estimates column widths from a data sample via Canvas measureText; SSR-safe fallback
  • SSR-safe — first N rows rendered on the server, hydration without layout shift
  • Zero external dependencies — only Vue 3 as peer dep
  • Tree-shakeable ESM + CJS dual build

Installation

bash
npm install vue-virtual-scroller-kit

Peer dependency:

bash
npm install vue@>=3.3

Quick start

vue
<script setup lang="ts">
import { VirtualList } from 'vue-virtual-scroller-kit'

interface Row {
  id: number
  text: string
}

const items: Row[] = Array.from({ length: 100_000 }, (_, i) => ({
  id: i,
  text: `Row ${i + 1}`,
}))
</script>

<template>
  <VirtualList :items="items" :estimated-item-size="48" style="height: 600px">
    <template #default="{ item, index }">
      <div style="padding: 12px 16px; border-bottom: 1px solid #eee">
        {{ index + 1 }}. {{ item.text }}
      </div>
    </template>
  </VirtualList>
</template>