A tiny but mighty list virtualization library, with zero dependencies 💪
About • Features • Installation • Usage • Examples • License
Instead of rendering all your data in a huge list, the virtual list component just renders the items that are visible, keeping your page nice and light.
This is heavily inspired by react-tiny-virtual-list and uses most of its code and functionality!
svelte-infinite-loading
compatibilityIf you're using this component in a Sapper application, make sure to install the package to
devDependencies
!
More Details
With npm:
$ npm install svelte-tiny-virtual-list
With yarn:
$ yarn add svelte-tiny-virtual-list
With pnpm (recommended):
$ npm i -g pnpm
$ pnpm install svelte-tiny-virtual-list
From CDN (via unpkg):
<!-- UMD -->
<script src="https://unpkg.com/svelte-tiny-virtual-list@^1/dist/svelte-tiny-virtual-list.js"></script>
<!-- ES Module -->
<script src="https://unpkg.com/svelte-tiny-virtual-list@^1/dist/svelte-tiny-virtual-list.mjs"></script>
<script>
import VirtualList from 'svelte-tiny-virtual-list';
const data = ['A', 'B', 'C', 'D', 'E', 'F' /* ... */];
</script>
<VirtualList width="100%" height={600} itemCount={data.length} itemSize={50}>
<div slot="item" let:index let:style {style}>
Letter: {data[index]}, Row: #{index}
</div>
</VirtualList>
Also works pretty well with svelte-infinite-loading
:
<script>
import VirtualList from 'svelte-tiny-virtual-list';
import InfiniteLoading from 'svelte-infinite-loading';
let data = ['A', 'B', 'C', 'D', 'E', 'F' /* ... */];
function infiniteHandler({ detail: { complete, error } }) {
try {
// Normally you'd make an http request here...
const newData = ['G', 'H', 'I', 'J', 'K', 'L' /* ... */];
data = [...data, ...newData];
complete();
} catch (e) {
error();
}
}
</script>
<VirtualList width="100%" height={600} itemCount={data.length} itemSize={50}>
<div slot="item" let:index let:style {style}>
Letter: {data[index]}, Row: #{index}
</div>
<div slot="footer">
<InfiniteLoading on:infinite={infiniteHandler} />
</div>
</VirtualList>
Property | Type | Required? | Description |
---|---|---|---|
width | number | string * |
✓ | Width of List. This property will determine the number of rendered items when scrollDirection is 'horizontal' . |
height | number | string * |
✓ | Height of List. This property will determine the number of rendered items when scrollDirection is 'vertical' . |
itemCount | number |
✓ | The number of items you want to render |
itemSize | number | number[] | (index: number) => number |
✓ | Either a fixed height/width (depending on the scrollDirection), an array containing the heights of all the items in your list, or a function that returns the height of an item given its index: (index: number): number |
scrollDirection | string |
Whether the list should scroll vertically or horizontally. One of 'vertical' (default) or 'horizontal' . |
|
scrollOffset | number |
Can be used to control the scroll offset; Also useful for setting an initial scroll offset | |
scrollToIndex | number |
Item index to scroll to (by forcefully scrolling if necessary) | |
scrollToAlignment | string |
Used in combination with scrollToIndex , this prop controls the alignment of the scrolled to item. One of: 'start' , 'center' , 'end' or 'auto' . Use 'start' to always align items to the top of the container and 'end' to align them bottom. Use 'center ' to align them in the middle of the container. 'auto' scrolls the least amount possible to ensure that the specified scrollToIndex item is fully visible. |
|
scrollToBehaviour | string |
Used in combination with scrollToIndex , this prop controls the behaviour of the scrolling. One of: 'auto' , 'smooth' or 'instant' (default). |
|
stickyIndices | number[] |
An array of indexes (eg. [0, 10, 25, 30] ) to make certain items in the list sticky (position: sticky ) |
|
overscanCount | number |
Number of extra buffer items to render above/below the visible items. Tweaking this can help reduce scroll flickering on certain browsers/devices. | |
estimatedItemSize | number |
Used to estimate the total size of the list before all of its items have actually been measured. The estimated total height is progressively adjusted as items are rendered. | |
getKey | (index: number) => any |
Function that returns the key of an item in the list, which is used to uniquely identify an item. This is useful for dynamic data coming from a database or similar. By default, it's using the item's index. |
* height
must be a number when scrollDirection
is 'vertical'
. Similarly, width
must be a number if scrollDirection
is 'horizontal'
item
- Slot for each itemindex: number
- Item indexstyle: string
- Item style, must be applied to the slot (look above for example)header
- Slot for the elements that should appear at the top of the listfooter
- Slot for the elements that should appear at the bottom of the list (e.g. InfiniteLoading
component from svelte-infinite-loading
)afterScroll
- Fired after handling the scroll eventdetail
Props:event: ScrollEvent
- The original scroll eventoffset: number
- Either the value of wrapper.scrollTop
or wrapper.scrollLeft
itemsUpdated
- Fired when the visible items are updateddetail
Props:start: number
- Index of the first visible itemend: number
- Index of the last visible itemrecomputeSizes(startIndex: number)
- This method force recomputes the item sizes after the specified index (these are normally cached).VirtualList
has no way of knowing when its underlying data has changed, since it only receives a itemSize property. If the itemSize is a number
, this isn't an issue, as it can compare before and after values and automatically call recomputeSizes
internally.
However, if you're passing a function to itemSize
, that type of comparison is error prone. In that event, you'll need to call recomputeSizes
manually to inform the VirtualList
that the size of its items has changed.
<script>
import { onMount } from 'svelte';
import VirtualList from 'svelte-tiny-virtual-list';
const data = ['A', 'B', 'C', 'D', 'E', 'F' /* ... */];
let virtualList;
function handleClick() {
virtualList.recomputeSizes(0);
}
</script>
<button on:click={handleClick}>Recompute Sizes</button>
<VirtualList
bind:this={virtualList}
width="100%"
height={600}
itemCount={data.length}
itemSize={50}
>
<div slot="item" let:index let:style {style}>
Letter: {data[index]}, Row: #{index}
</div>
</VirtualList>
You can style the elements of the virtual list like this:
<script>
import VirtualList from 'svelte-tiny-virtual-list';
const data = ['A', 'B', 'C', 'D', 'E', 'F' /* ... */];
</script>
<div class="list">
<VirtualList width="100%" height={600} itemCount={data.length} itemSize={50}>
<div slot="item" let:index let:style {style}>
Letter: {data[index]}, Row: #{index}
</div>
</VirtualList>
</div>
<style>
.list :global(.virtual-list-wrapper) {
background-color: #0f0;
/* ... */
}
.list :global(.virtual-list-inner) {
background-color: #f00;
/* ... */
}
</style>