Scroller

Storybook

A scroller component that allows blazing fast scrolling of any amount of data.

Preview

Default Scroller


<dt-scroller
 :items="items"
 :item-size="32"
 :scroller-height="200"
 :scroller-width="300"
 >
 <template #default="{ item }">
   <div class="user">
     {{ item.name }}
   </div>
 </template>
</dt-scroller>

Dynamic Scroller


<dt-scroller
 :items="dynamicItems"
 :min-item-size="54"
 :scroller-height="300"
 :scroller-width="500"
 :dynamic="true"
 >
 <template #default="{ item }">
   <div class="avatar">
     {{ item.id }}
     <img
      :key="item.avatar"
      :src="item.avatar"
      alt="avatar"
      class="image"
     >
  </div>
  <div class="text">
     {{ item.message }}
  </div>
 </template>
</dt-scroller>

Infinite Scroll

Use the after slot for a loading indicator that scrolls with the list, and scroll-end to fetch the next page. scroll-end fires when the last item enters the rendered view pool — at least one buffer length before the viewport's end edge, earlier still if before/after slot content adds to the window — so the fetch starts before the user runs out of content.

scroll-start is the mirror image, for prepending older items ahead of the viewport's start edge.

The scroller recomputes its rendered window on mount and on scroll. It does not watch items, so after you append a page call updateItems() on the component ref — otherwise the new rows will not appear until the next scroll.

Assign a new array rather than mutating the existing one in place. The scroller itself does not mind either way, but watching items by reference is the usual way a wrapper component triggers the updateItems() call for you, and a reference watcher never sees an in-place push.

<dt-scroller
 ref="scroller"
 :items="items"
 :item-size="32"
 :buffer="400"
 :scroller-height="200"
 @scroll-end="fetchNextPage"
 >
 <template #default="{ item }">
   <div class="user">
     {{ item.name }}
   </div>
 </template>
 <template #after>
   <dt-loader v-if="isFetching" />
 </template>
</dt-scroller>
async function fetchNextPage () {
  if (isFetching.value) return;
  isFetching.value = true;
  try {
    const page = await fetchMore();
    items.value = [...items.value, ...page];
    await nextTick();
    scroller.value.updateItems();
  } finally {
    isFetching.value = false;
  }
}

The empty slot only renders while the list is empty.

Vue API

Slots

Name
Description
after

Content rendered after the items, inside the scroll viewport. Scrolls with the list.

before

Content rendered before the items, inside the scroll viewport. Scrolls with the list.

default
empty

Content rendered inside the list wrapper only while the list is empty.

Props

Name
Description
Default
items
required

The items to render. If the items are simple arrays, the index will be used as the key. If the items are objects, the keyField will be used as the key.

Type: array
buffer

Amount of pixels added to each edge of the scroll viewport, so items are rendered before they scroll into view. Also controls how early scroll-start and scroll-end fire relative to the viewport edges.

Type: number
200
direction

The direction of the scroller.

Type: string
Values: verticalhorizontal
'vertical'
dynamic

Indicates if the items need to react to changes in their size. If disabled the itemSize prop is required and you will get improved performance. If enabled the minItemSize prop is required and you will have reduced performance but the ability to reactively size list items

Type: boolean
Values: truefalse
false
itemSize

Display height (or width in horizontal mode) of the items in pixels used to calculate the scroll size and position. Required if DYNAMIC is false

Type: number
null
itemTag

The tag to use for the items.

Type: string
'div'
keyField

The key field to use for the items. If the items are objects, the scroller needs to be able to identify them. By default it will look for an id field on the items. This can be configured with this prop if you are using another field name.

Type: string
'id'
listTag

The tag to use for the list.

Type: string
'div'
minItemSize

Minimum size used if the height (or width in horizontal mode) of a item is unknown. Is required for the initial render of items in DYNAMIC size mode.

Type: number|string
null
scrollerHeight

The height of the scroller. Can be a number (in pixels) or a string (in CSS units).

Type: string|number
'100%'
scrollerWidth

The width of the scroller. Can be a number (in pixels) or a string (in CSS units).

Type: string|number
'100%'

Events

Name
Description
scroll-end

Emitted when the last item enters the rendered view pool. Fires at least one buffer length before the viewport's end edge is reached (earlier still with after slot content), so callers can append items ahead of time.

scroll-start

Emitted when the first item enters the rendered view pool. Fires at least one buffer length before the viewport's start edge is reached (earlier still with before slot content), so callers can prepend items ahead of time.

user-position

Describe when the scroller changes from start/middle/end

Scroller documentation last updated Friday, August 28, 2026