VirtualScrollbar
A themable custom scrollbar overlay. Decoupled from useVirtualScroll — it works with the scroll element exposed by any component above (via getScrollElement()), or any scrollable element you pass directly. Renders a track + draggable thumb sized and positioned from scrollHeight/clientHeight/scrollTop, and drags with pointer events.
The thumb is a full role="scrollbar" widget: aria-orientation/aria-valuemin/aria-valuemax/aria-valuenow reflect the current scroll position, and it's keyboard-operable — Arrow keys step, Home/End jump to the start/end, PageUp/PageDown jump by a viewport's worth.
Props
target
() => HTMLElement | null
Returns the scroll element to sync with.
orientation
'vertical' | 'horizontal' · default: 'vertical'
Scroll axis to track.
minThumbSize
number · default: 24
Minimum thumb size in px, so a huge list doesn't shrink it to an ungrabbable sliver.
Exposed API
refresh()
Force-reattach and recompute against the current target() — useful after a manual DOM swap that the component's own ResizeObserver/scroll listeners wouldn't otherwise pick up.
scrollbarRef.value?.refresh()CSS custom properties
--vvsk-scrollbar-size
Default: 10px
Thickness of the track/thumb.
--vvsk-scrollbar-track
Default: transparent
Track background.
--vvsk-scrollbar-thumb
Default: rgb(255 255 255 / 25%)
Thumb background.
--vvsk-scrollbar-thumb-hover
Default: rgb(255 255 255 / 40%)
Thumb background while hovered/dragged.
Example
<script setup lang="ts">
import { ref } from 'vue'
import { VirtualList, VirtualScrollbar } from 'vue-virtual-scroller-kit'
import type { VirtualListExpose } from 'vue-virtual-scroller-kit'
const listRef = ref<VirtualListExpose | null>(null)
const items = Array.from({ length: 10_000 }, (_, i) => ({ id: i, text: `Row ${i + 1}` }))
</script>
<template>
<div style="position: relative; display: flex; height: 500px">
<VirtualList
ref="listRef"
:items="items"
:estimated-item-size="48"
class="vvsk-scrollbar-hidden"
style="flex: 1"
>
<template #default="{ item }">
<div style="padding: 12px 16px; border-bottom: 1px solid #eee">{{ item.text }}</div>
</template>
</VirtualList>
<VirtualScrollbar :target="() => listRef?.getScrollElement() ?? null" />
</div>
</template>Hide the native scrollbar on the paired container when using VirtualScrollbar (optional — the two can coexist if you want both):
.vvsk-scrollbar-hidden {
scrollbar-width: none; /* Firefox */
}
.vvsk-scrollbar-hidden::-webkit-scrollbar {
display: none; /* Chrome, Safari, Edge */
}