A high-performance virtual list component for Svelte 5 applications that efficiently renders large datasets with minimal memory usage.
- π Dynamic item height handling - no fixed height required
- π Bi-directional scrolling support (top-to-bottom and bottom-to-top)
- π Automatic resize handling for dynamic content
- π TypeScript support with full type safety
- π SSR compatible with hydration support
- β¨ Svelte 5 runes and snippets support
- π¨ Customizable styling with class props
- π Debug mode for development
- π― Smooth scrolling with configurable buffer zones
- π§ Memory-optimized for 10k+ items
- π§ͺ Comprehensive test coverage (vitest and playwright)
- π Progressive initialization for large datasets
- πΉοΈ Programmatic scrolling with
scroll
You can now programmatically scroll to any item in the list using the scroll
method. This is useful for chat apps, jump-to-item navigation, and more. You can check the usage in src/routes/tests/scroll
. Thank you for the feature request!
<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() {
// Scroll to item 5000 with smooth scrolling and auto alignment
listRef.scroll({ index: 5000, smoothScroll: true, align: 'auto' })
}
</script>
<button on:click={goToItem5000}> Scroll to item 5000 </button>
<SvelteVirtualList {items} bind:this={listRef}>
{#snippet renderItem(item)}
<div>{item.text}</div>
{/snippet}
</SvelteVirtualList>
scroll(options: { index: number; smoothScroll?: boolean; shouldThrowOnBounds?: boolean; align?: 'auto' | 'top' | 'bottom' | 'nearest' })
index
: The item index to scroll to (0-based)smoothScroll
: If true, uses smooth scrolling (default: true)shouldThrowOnBounds
: If true, throws if index is out of bounds (default: true)align
: Where to align the item in the viewport:'auto'
(default): Only scroll if not visible, align to top or bottom as appropriate'top'
: Always align to the top'bottom'
: Always align to the bottom'nearest'
: Scroll as little as possible to bring the item into view (like native scrollIntoView({ block: 'nearest' }))
<button on:click={() => listRef.scroll({ index: 5000, align: 'nearest' })}>
Scroll to item 5000 (nearest)
</button>
npm install @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>
<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" debug>
{#snippet renderItem(message)}
<div class="message-container">
<p>{message.text}</p>
<span class="timestamp">
{message.timestamp.toLocaleString()}
</span>
</div>
{/snippet}
</SvelteVirtualList>
</div>
Use mode="bottomToTop"
for chat-like lists anchored to the bottom. Programmatic scrolling uses the same API as top-to-bottom lists:
<script lang="ts">
import SvelteVirtualList from '@humanspeak/svelte-virtual-list'
let listRef
const messages = Array.from({ length: 2000 }, (_, i) => ({ id: i, text: `Msg ${i}` }))
</script>
<SvelteVirtualList items={messages} mode="bottomToTop" bind:this={listRef} />
<button on:click={() => listRef.scroll({ index: messages.length - 1, align: 'bottom' })}>
Jump to latest
</button>
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) |
- Unit tests (Vitest):
npm test
- E2E tests (Playwright):
- One-time:
npx playwright install
- Run:
npm run test:e2e
- One-time:
- The
bufferSize
prop affects memory usage and scroll smoothness - Items are measured and cached for optimal performance
- Dynamic height calculations happen automatically
- Resize observers handle container/content changes
- Virtual DOM updates are batched for efficiency
MIT Β© Humanspeak, Inc.
Made with β₯ by Humanspeak