tanstack-vue-virtual-skilld

Headless UI for virtualizing scrollable elements in Vue. ALWAYS use when writing code importing "@tanstack/vue-virtual". Consult for debugging, best practices, or modifying @tanstack/vue-virtual, tanstack/vue-virtual, tanstack vue-virtual, tanstack vue virtual, virtual.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "tanstack-vue-virtual-skilld" with this command: npx skills add harlan-zw/vue-ecosystem-skills/harlan-zw-vue-ecosystem-skills-tanstack-vue-virtual-skilld

TanStack/virtual @tanstack/vue-virtual

Headless UI for virtualizing scrollable elements in Vue

Version: 3.13.22 (Mar 2026) Deps: @tanstack/virtual-core@3.13.22 Tags: beta: 3.0.0-beta.68 (Oct 2023), latest: 3.13.22 (Mar 2026)

References: Docs — API reference, guides

API Changes

This section documents version-specific API changes — prioritize recent major/minor releases.

  • BREAKING: useVirtualizer — replaces useVirtual in v3 migration; older positional arguments or v2 option names are no longer supported source

  • BREAKING: Ref<Virtualizer> return type — v3 useVirtualizer returns a Vue Ref instead of a raw object; instance methods must be accessed via .value (e.g., rowVirtualizer.value.getVirtualItems())

  • BREAKING: count — replaces size option in v3 migration; using size will result in zero items being virtualized source

  • BREAKING: getScrollElement — replaces parentRef option in v3 migration; must be a function that returns the scrollable element or null source

  • BREAKING: measureElement — replaces measureRef pattern from v2; you must now pass virtualizer.value.measureElement to the ref attribute and set data-index on the element source

  • NEW: getTotalSize() auto-updates — as of v3.13.13, the virtualizer automatically notifies the framework when the count option changes, ensuring getTotalSize() and the UI update correctly without manual workarounds for filtering or search source

  • NEW: lanes — new in v3; allows dividing the list into multiple columns (vertical) or rows (horizontal) to support grid-like or masonry layouts source

  • NEW: gap — new in v3; specifies the spacing between items in pixels, removing the need for manual margin or padding calculations source

  • NEW: useWindowVirtualizer — specialized adapter for window-based scrolling; simplifies configuration when the browser window is the scroll container source

  • NEW: scrollMargin — allows specifying the offset between the scroll container's start and the beginning of the virtualized list; essential for lists preceded by headers source

  • NEW: isRtl — built-in support for right-to-left language locales; inverts horizontal scrolling logic when enabled source

  • NEW: useScrollendEvent — utilizes the native scrollend event where available to reset isScrolling state, falling back to a debounced timer if disabled source

  • NEW: shouldAdjustScrollPositionOnItemSizeChange — provides fine-grained control over scroll position adjustments when dynamic items differ from their estimated size source

  • NEW: useAnimationFrameWithResizeObserver — added in v3.13.x; defers ResizeObserver measurement processing to the next animation frame to batch DOM mutations source

Also changed: isScrollingResetDelay new in v3 · rangeExtractor now receives Range object · VirtualItem adds lane property · resizeItem method for manual size overrides · enabled option to pause observers

Best Practices

  • Account for scrollMargin in absolute positioning — when using a shared scroll container with static headers, subtract the margin from the item's start position to maintain correct layout source
<script setup>
const rowVirtualizer = useVirtualizer({
  count: 1000,
  scrollMargin: 100, // Height of header
  // ...
})
</script>

<template>
  <div v-for="item in rowVirtualizer.getVirtualItems()" :key="item.key"
    :style="{
      transform: `translateY(${item.start - rowVirtualizer.options.scrollMargin}px)`
    }"
  >
    {{ item.index }}
  </div>
</template>
  • Overestimate estimateSize for dynamic elements — providing a "maximum likely" size prevents the scrollbar from jumping and items from "resetting" their position when scrolling upwards into unmeasured territory source

  • Implement shouldAdjustScrollPositionOnItemSizeChange for chat/messaging UIs — use this callback to control scroll adjustments when prepending items, preventing visual jumps as new elements are measured source

  • Attach data-index when using measureElement — the virtualizer requires this attribute on the measured DOM element to correctly map the size back to the item's internal state source

<div
  v-for="item in virtualizer.getVirtualItems()"
  :key="item.key"
  :data-index="item.index"
  :ref="virtualizer.measureElement"
>
  {{ item.index }}
</div>
  • Pass configuration via computed or Ref — the Vue adapter's useVirtualizer watch-triggers setOptions automatically, allowing the instance to reactively update count or overscan without manual re-instantiation

  • Avoid useAnimationFrameWithResizeObserver for performance — native ResizeObserver is already batched; enabling this adds a ~16ms delay that can cause visual flickering or stale measurements during fast scrolls source

  • Provide a stable getItemKey for persistent state — using a unique identifier (like a database ID) instead of the default index ensures that item state (focus, internal refs) is preserved during reorders or filtering source

  • Wrap initial scrollToIndex in requestAnimationFrame — for "scroll-to-bottom" initialization (e.g. chat), deferring the scroll ensures the DOM is rendered and initial measurements are processed by the virtualizer source

  • Use built-in gap over manual CSS margins — the gap option ensures the virtualizer accounts for item spacing in its internal getTotalSize() calculation, which manual margins do not source

  • Pause observers with enabled: false — instead of unmounting the virtualizer, toggle the enabled option to pause monitoring (e.g., when a tab is hidden). This preserves existing measurements while saving CPU cycles source

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

vue-skilld

No summary provided by upstream source.

Repository SourceNeeds Review
General

pinia-skilld

No summary provided by upstream source.

Repository SourceNeeds Review
General

vueuse-core-skilld

No summary provided by upstream source.

Repository SourceNeeds Review
General

vue-router-skilld

No summary provided by upstream source.

Repository SourceNeeds Review