Virtualize Svelte Table final row or column models with reactive counts and scroll targets, stable keys, dynamic measurement, absolute transforms, sticky regions, grid/flex sizing, and infinite data.
67
82%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
This skill builds on @tanstack/table-core#core, getting-started, and table-state. Virtual is a rendering layer over Table’s final model, never a tableFeatures plugin.
<script lang="ts">
import { get } from 'svelte/store'
import { createVirtualizer } from '@tanstack/svelte-virtual'
let scrollElement = $state<HTMLDivElement>()
const rows = $derived(table.getRowModel().rows)
const rowVirtualizer = createVirtualizer({
count: rows.length,
getScrollElement: () => scrollElement ?? null,
estimateSize: () => 34,
getItemKey: (index) => rows[index]!.id,
overscan: 5,
})
// The store adapter does not track getter options. Push reactive inputs.
$effect(() => {
get(rowVirtualizer).setOptions({
count: rows.length,
getScrollElement: () => scrollElement ?? null,
})
})
</script>
<div
bind:this={scrollElement}
style="height: 500px; overflow: auto; position: relative"
>
<div style:height={`${$rowVirtualizer.getTotalSize()}px`}>
{#each $rowVirtualizer.getVirtualItems() as item (item.key)}
<div style={`position:absolute;transform:translateY(${item.start}px)`}>
{rows[item.index].id}
</div>
{/each}
</div>
</div>Use table.getRowModel().rows for rows and table.getVisibleLeafColumns() for columns. Recompute counts when filtering, sorting, expansion, or visibility changes.
Use one scroll container, a total-size spacer, positioned items, and either fixed estimates or measureElement. For semantic tables with dynamic rows, follow the maintained grid/flex examples rather than assuming native table layout will honor transforms.
In infinite scrolling, compare the last virtual item with fetched row count, then request the next Query page only when more server rows exist and no fetch is active.
Wrong:
const rows = dataCorrect:
const rows = $derived(table.getRowModel().rows)Raw data ignores Table filtering, sorting, grouping, expansion, and pagination decisions.
Source: examples/svelte/virtualized-rows/src/App.svelte
Wrong:
const virtualizer = createVirtualizer({
get count() {
return rows.length
},
getScrollElement,
})Correct:
const virtualizer = createVirtualizer({ count: rows.length, getScrollElement })
$effect(() => {
get(virtualizer).setOptions({
count: rows.length,
getScrollElement,
})
})createVirtualizer returns a Svelte store, and its adapter does not track getter options. Push rune-derived counts and the bound scroll element with $effect and get(store).setOptions(...).
Source: examples/svelte/virtualized-rows/src/App.svelte
Wrong:
{#each rowVirtualizer.getVirtualItems() as item}<div>
{rows[item.index].id}
</div>{/each}Correct:
<div style:height={`${$rowVirtualizer.getTotalSize()}px`}>
<div style="position:absolute"></div>
</div>Virtual supplies ranges and measurements, not spacer height, transforms, sticky regions, or column widths. In markup, call virtualizer methods through the store auto-subscription ($rowVirtualizer); use get(rowVirtualizer) in script code.
Source: docs/framework/svelte/guide/virtualization.md
Inspect installed @tanstack/svelte-table/dist/ for Table APIs and @tanstack/svelte-virtual/dist/ for the exact virtualizer options. Use the maintained Svelte examples for layout combinations.
9e523bc
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.