A high-performance virtual list component for Svelte 5 applications that efficiently renders large datasets with minimal memory usage.
scrollonLoadMore# Using pnpm (recommended)
pnpm add @humanspeak/svelte-virtual-list
# Using npm
npm install @humanspeak/svelte-virtual-list
# Using yarn
yarn add @humanspeak/svelte-virtual-list
<script lang="ts">
import SvelteVirtualList from '@humanspeak/svelte-virtual-list'
const items = Array.from({ length: 1000 }, (_, i) => ({
id: i,
text: `Item ${i}`
}))
</script>
<SvelteVirtualList {items}>
{#snippet renderItem(item)}
<div>{item.text}</div>
{/snippet}
</SvelteVirtualList>
| Prop | Type | Default | Description |
|---|---|---|---|
items |
T[] |
Required | Array of items to render |
defaultEstimatedItemHeight |
number |
40 |
Initial height estimate used until items are measured |
mode |
'topToBottom' | 'bottomToTop' |
'topToBottom' |
Scroll direction and anchoring behavior |
bufferSize |
number |
20 |
Number of items rendered outside the viewport |
debug |
boolean |
false |
Enable debug logging and visualizations |
containerClass |
string |
'' |
Class for outer container |
viewportClass |
string |
'' |
Class for scrollable viewport |
contentClass |
string |
'' |
Class for content wrapper |
itemsClass |
string |
'' |
Class for items container |
testId |
string |
'' |
Base test id used in internal test hooks (useful for E2E/tests and debugging) |
onLoadMore |
() => void | Promise<void> |
- | Callback when more data is needed for infinite scroll |
loadMoreThreshold |
number |
20 |
Items from end to trigger onLoadMore |
hasMore |
boolean |
true |
Set to false when all data has been loaded |
Use mode="bottomToTop" for chat-like lists anchored to the bottom:
<script lang="ts">
import SvelteVirtualList from '@humanspeak/svelte-virtual-list'
type Message = {
id: number
text: string
timestamp: Date
}
const messages: Message[] = Array.from({ length: 100 }, (_, i) => ({
id: i,
text: `Message ${i}`,
timestamp: new Date()
}))
</script>
<div style="height: 500px;">
<SvelteVirtualList items={messages} mode="bottomToTop">
{#snippet renderItem(message)}
<div class="message-container">
<p>{message.text}</p>
<span class="timestamp">
{message.timestamp.toLocaleString()}
</span>
</div>
{/snippet}
</SvelteVirtualList>
</div>
Scroll to any item in the list using the scroll method. Useful for chat apps, jump-to-item navigation, and more.
<script lang="ts">
import SvelteVirtualList from '@humanspeak/svelte-virtual-list'
let listRef
const items = Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}` }))
function goToItem5000() {
listRef.scroll({ index: 5000, smoothScroll: true, align: 'auto' })
}
</script>
<button onclick={goToItem5000}> Scroll to item 5000 </button>
<SvelteVirtualList {items} bind:this={listRef}>
{#snippet renderItem(item)}
<div>{item.text}</div>
{/snippet}
</SvelteVirtualList>
| Option | Type | Default | Description |
|---|---|---|---|
index |
number |
Required | The item index to scroll to (0-based) |
smoothScroll |
boolean |
true |
Use smooth scrolling animation |
shouldThrowOnBounds |
boolean |
true |
Throw if index is out of bounds |
align |
'auto' | 'top' | 'bottom' | 'nearest' |
'auto' |
Where to align the item in the viewport |
Alignment options:
'auto' - Only scroll if not visible, align to nearest edge'top' - Always align to the top'bottom' - Always align to the bottom'nearest' - Scroll as little as possible to bring the item into viewWorks with both topToBottom and bottomToTop modes:
<SvelteVirtualList items={messages} mode="bottomToTop" bind:this={listRef} />
<button onclick={() => listRef.scroll({ index: messages.length - 1, align: 'bottom' })}>
Jump to latest
</button>
Load more data automatically as users scroll near the end of the list. Perfect for paginated APIs, infinite feeds, and chat applications.
<script lang="ts">
import SvelteVirtualList from '@humanspeak/svelte-virtual-list'
let items = $state([...initialItems])
let hasMore = $state(true)
async function loadMore() {
const newItems = await fetchMoreItems()
items = [...items, ...newItems]
if (newItems.length === 0) {
hasMore = false
}
}
</script>
<SvelteVirtualList {items} onLoadMore={loadMore} loadMoreThreshold={20} {hasMore}>
{#snippet renderItem(item)}
<div>{item.text}</div>
{/snippet}
</SvelteVirtualList>
onLoadMore calls while loadingtopToBottom and bottomToTop modesbufferSize prop affects memory usage and scroll smoothness# Run unit tests with coverage
pnpm test
# Run specific test files
pnpm vitest src/lib/utils/throttle.test.ts
# Install Playwright browsers (one-time setup)
npx playwright install
# Run all e2e tests
pnpm run test:e2e
# Run specific e2e test
npx playwright test tests/docs-visit.spec.ts --project=chromium
# Debug mode
npx playwright test --debug
This is a PNPM workspace with two packages:
./ - Main Svelte Virtual List component package./docs - Documentation site with live demos and examples# Install dependencies for both packages
pnpm install
# Start development server
pnpm dev
# Start both package and docs
pnpm run dev:all
# Build package
pnpm run build
# Check TypeScript/Svelte
pnpm run check
# Format and lint code (uses Trunk)
trunk fmt
trunk check
# Run all tests
pnpm test:all
This project uses Trunk for formatting and linting. Trunk manages tool versions and runs checks automatically via pre-commit hooks.
MIT Β© Humanspeak, Inc.
Made with β€οΈ by Humanspeak