{"id":24010592,"url":"https://github.com/humanspeak/svelte-virtual-list","last_synced_at":"2026-05-26T21:01:10.221Z","repository":{"id":270857322,"uuid":"911408497","full_name":"humanspeak/svelte-virtual-list","owner":"humanspeak","description":"📜 A performant virtual list/scrolling component for Svelte applications - efficiently render large scrollable lists with minimal memory usage","archived":false,"fork":false,"pushed_at":"2026-04-14T19:05:59.000Z","size":6201,"stargazers_count":79,"open_issues_count":3,"forks_count":9,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-14T20:29:21.604Z","etag":null,"topics":["infinite-scroll","javascript","performance","svelte","sveltekit","typescript","ui-components","virtual-list","virtual-scroll","web-components"],"latest_commit_sha":null,"homepage":"https://virtuallist.svelte.page","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/humanspeak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["humanspeak"]}},"created_at":"2025-01-03T00:24:46.000Z","updated_at":"2026-04-14T20:21:04.000Z","dependencies_parsed_at":"2025-01-17T16:36:52.154Z","dependency_job_id":"5707ac19-fcd4-4e7e-9f4b-fda43e1d0a2e","html_url":"https://github.com/humanspeak/svelte-virtual-list","commit_stats":null,"previous_names":["humanspeak/svelte-virtual-list"],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/humanspeak/svelte-virtual-list","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-virtual-list","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-virtual-list/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-virtual-list/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-virtual-list/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/humanspeak","download_url":"https://codeload.github.com/humanspeak/svelte-virtual-list/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-virtual-list/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33538660,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"ssl_error","status_checked_at":"2026-05-26T15:22:15.568Z","response_time":63,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["infinite-scroll","javascript","performance","svelte","sveltekit","typescript","ui-components","virtual-list","virtual-scroll","web-components"],"created_at":"2025-01-08T04:15:34.077Z","updated_at":"2026-05-26T21:01:10.215Z","avatar_url":"https://github.com/humanspeak.png","language":"TypeScript","funding_links":["https://github.com/sponsors/humanspeak"],"categories":[],"sub_categories":[],"readme":"# @humanspeak/svelte-virtual-list\n\n[![NPM version](https://img.shields.io/npm/v/@humanspeak/svelte-virtual-list.svg)](https://www.npmjs.com/package/@humanspeak/svelte-virtual-list)\n[![Build Status](https://github.com/humanspeak/svelte-virtual-list/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/humanspeak/svelte-virtual-list/actions/workflows/npm-publish.yml)\n[![Coverage Status](https://coveralls.io/repos/github/humanspeak/svelte-virtual-list/badge.svg?branch=main)](https://coveralls.io/github/humanspeak/svelte-virtual-list?branch=main)\n[![License](https://img.shields.io/npm/l/@humanspeak/svelte-virtual-list.svg)](https://github.com/humanspeak/svelte-virtual-list/blob/main/LICENSE)\n[![Downloads](https://img.shields.io/npm/dm/@humanspeak/svelte-virtual-list.svg)](https://www.npmjs.com/package/@humanspeak/svelte-virtual-list)\n[![CodeQL](https://github.com/humanspeak/svelte-virtual-list/actions/workflows/codeql.yml/badge.svg)](https://github.com/humanspeak/svelte-virtual-list/actions/workflows/codeql.yml)\n[![Install size](https://packagephobia.com/badge?p=@humanspeak/svelte-virtual-list)](https://packagephobia.com/result?p=@humanspeak/svelte-virtual-list)\n[![Code Style: Trunk](https://img.shields.io/badge/code%20style-trunk-blue.svg)](https://trunk.io)\n[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)\n[![Types](https://img.shields.io/npm/types/@humanspeak/svelte-virtual-list.svg)](https://www.npmjs.com/package/@humanspeak/svelte-virtual-list)\n[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/humanspeak/svelte-virtual-list/graphs/commit-activity)\n\nA high-performance virtual list component for Svelte 5 applications that efficiently renders large datasets with minimal memory usage.\n\n## Features\n\n- 📏 Dynamic item height handling - no fixed height required\n- 🔄 Automatic resize handling for dynamic content\n- 📝 TypeScript support with full type safety\n- 🚀 SSR compatible with hydration support\n- ✨ Svelte 5 runes and snippets support\n- 🎨 Customizable styling with class props\n- 🐛 Debug mode for development\n- 🎯 Smooth scrolling with configurable buffer zones\n- 🧠 Memory-optimized for 10k+ items\n- 🧪 Comprehensive test coverage (vitest and playwright)\n- 🚀 Progressive initialization for large datasets\n- 🕹️ Programmatic scrolling with `scroll`\n- ♾️ Infinite scroll support with `onLoadMore`\n\n## Requirements\n\n- Svelte 5\n- Node.js 18+\n\n## Installation\n\n```bash\n# Using pnpm (recommended)\npnpm add @humanspeak/svelte-virtual-list\n\n# Using npm\nnpm install @humanspeak/svelte-virtual-list\n\n# Using yarn\nyarn add @humanspeak/svelte-virtual-list\n```\n\n## Basic Usage\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n    import SvelteVirtualList from '@humanspeak/svelte-virtual-list'\n\n    const items = Array.from({ length: 1000 }, (_, i) =\u003e ({\n        id: i,\n        text: `Item ${i}`\n    }))\n\u003c/script\u003e\n\n\u003cSvelteVirtualList {items}\u003e\n    {#snippet renderItem(item)}\n        \u003cdiv\u003e{item.text}\u003c/div\u003e\n    {/snippet}\n\u003c/SvelteVirtualList\u003e\n```\n\n## Props\n\n| Prop                         | Type                          | Default  | Description                                                                   |\n| ---------------------------- | ----------------------------- | -------- | ----------------------------------------------------------------------------- |\n| `items`                      | `T[]`                         | Required | Array of items to render                                                      |\n| `defaultEstimatedItemHeight` | `number`                      | `40`     | Initial height estimate used until items are measured                         |\n| `bufferSize`                 | `number`                      | `20`     | Number of items rendered outside the viewport                                 |\n| `debug`                      | `boolean`                     | `false`  | Enable debug logging and visualizations                                       |\n| `containerClass`             | `string`                      | `''`     | Class for outer container                                                     |\n| `viewportClass`              | `string`                      | `''`     | Class for scrollable viewport                                                 |\n| `contentClass`               | `string`                      | `''`     | Class for content wrapper                                                     |\n| `itemsClass`                 | `string`                      | `''`     | Class for items container                                                     |\n| `testId`                     | `string`                      | `''`     | Base test id used in internal test hooks (useful for E2E/tests and debugging) |\n| `onLoadMore`                 | `() =\u003e void \\| Promise\u003cvoid\u003e` | -        | Callback when more data is needed for infinite scroll                         |\n| `loadMoreThreshold`          | `number`                      | `20`     | Items from end to trigger `onLoadMore`                                        |\n| `hasMore`                    | `boolean`                     | `true`   | Set to `false` when all data has been loaded                                  |\n\n## Programmatic Scrolling\n\nScroll to any item in the list using the `scroll` method. Useful for jump-to-item navigation, search results, and more.\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n    import SvelteVirtualList from '@humanspeak/svelte-virtual-list'\n    let listRef\n    const items = Array.from({ length: 10000 }, (_, i) =\u003e ({ id: i, text: `Item ${i}` }))\n\n    function goToItem5000() {\n        listRef.scroll({ index: 5000, smoothScroll: true, align: 'auto' })\n    }\n\u003c/script\u003e\n\n\u003cbutton onclick={goToItem5000}\u003e Scroll to item 5000 \u003c/button\u003e\n\u003cSvelteVirtualList {items} bind:this={listRef}\u003e\n    {#snippet renderItem(item)}\n        \u003cdiv\u003e{item.text}\u003c/div\u003e\n    {/snippet}\n\u003c/SvelteVirtualList\u003e\n```\n\n### scroll() Options\n\n| Option                | Type                                       | Default  | Description                             |\n| --------------------- | ------------------------------------------ | -------- | --------------------------------------- |\n| `index`               | `number`                                   | Required | The item index to scroll to (0-based)   |\n| `smoothScroll`        | `boolean`                                  | `true`   | Use smooth scrolling animation          |\n| `shouldThrowOnBounds` | `boolean`                                  | `true`   | Throw if index is out of bounds         |\n| `align`               | `'auto' \\| 'top' \\| 'bottom' \\| 'nearest'` | `'auto'` | Where to align the item in the viewport |\n\nAlignment options:\n\n- `'auto'` - Only scroll if not visible, align to nearest edge\n- `'top'` - Always align to the top\n- `'bottom'` - Always align to the bottom\n- `'nearest'` - Scroll as little as possible to bring the item into view\n\n## Infinite Scroll\n\nLoad more data automatically as users scroll near the end of the list. Perfect for paginated APIs, infinite feeds, and activity logs.\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n    import SvelteVirtualList from '@humanspeak/svelte-virtual-list'\n\n    let items = $state([...initialItems])\n    let hasMore = $state(true)\n\n    async function loadMore() {\n        const newItems = await fetchMoreItems()\n        items = [...items, ...newItems]\n        if (newItems.length === 0) {\n            hasMore = false\n        }\n    }\n\u003c/script\u003e\n\n\u003cSvelteVirtualList {items} onLoadMore={loadMore} loadMoreThreshold={20} {hasMore}\u003e\n    {#snippet renderItem(item)}\n        \u003cdiv\u003e{item.text}\u003c/div\u003e\n    {/snippet}\n\u003c/SvelteVirtualList\u003e\n```\n\n### Infinite Scroll Behavior\n\n- Triggers when scrolling near the end of the list\n- Automatically triggers on mount if initial items are below threshold\n- Prevents concurrent `onLoadMore` calls while loading\n- Works with both sync and async callbacks\n\n### Integration Guides\n\n- [Infinite Scroll with Convex](documentation/CONVEX_INFINITE_SCROLL.md) - Real-time data + pagination with Convex backend\n\n## Performance Considerations\n\n- The `bufferSize` prop affects memory usage and scroll smoothness\n- Items are measured and cached for optimal performance\n- Dynamic height calculations happen automatically\n- Resize observers handle container/content changes\n- Virtual DOM updates are batched for efficiency\n\n## Testing\n\n### Unit Tests (Vitest)\n\n```bash\n# Run unit tests with coverage\npnpm test\n\n# Run specific test files\npnpm vitest src/lib/utils/throttle.test.ts\n```\n\n### E2E Tests (Playwright)\n\n```bash\n# Install Playwright browsers (one-time setup)\nnpx playwright install\n\n# Run all e2e tests\npnpm run test:e2e\n\n# Run specific e2e test\nnpx playwright test tests/docs-visit.spec.ts --project=chromium\n\n# Debug mode\nnpx playwright test --debug\n```\n\n## Project Structure\n\nThis is a **PNPM workspace** with two packages:\n\n1. **`./`** - Main Svelte Virtual List component package\n2. **`./docs`** - Documentation site with live demos and examples\n\n### Development Commands\n\n```bash\n# Install dependencies for both packages\npnpm install\n\n# Start development server\npnpm dev\n\n# Start both package and docs\npnpm run dev:all\n\n# Build package\npnpm run build\n\n# Check TypeScript/Svelte\npnpm run check\n\n# Format and lint code (uses Trunk)\ntrunk fmt\ntrunk check\n\n# Run all tests\npnpm test:all\n```\n\nThis project uses [Trunk](https://trunk.io) for formatting and linting. Trunk manages tool versions and runs checks automatically via pre-commit hooks.\n\n## License\n\nMIT © [Humanspeak, Inc.](LICENSE)\n\n## Credits\n\nMade with ❤️ by [Humanspeak](https://humanspeak.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhumanspeak%2Fsvelte-virtual-list","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhumanspeak%2Fsvelte-virtual-list","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhumanspeak%2Fsvelte-virtual-list/lists"}