---
name: with-tanstack-virtual
description: >
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.
metadata:
type: composition
library: '@tanstack/svelte-table'
framework: svelte
library_version: '9.1.0'
requires:
- '@tanstack/table-core#core'
- getting-started
- table-state
sources:
- 'TanStack/table:docs/framework/svelte/guide/virtualization.md'
- 'TanStack/table:examples/svelte/virtualized-rows'
- 'TanStack/table:examples/svelte/virtualized-columns'
- 'TanStack/table:examples/svelte/virtualized-infinite-scrolling'
---
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.
## Setup
```svelte
{#each $rowVirtualizer.getVirtualItems() as item (item.key)}
{rows[item.index].id}
{/each}
```
## Core Patterns
### Virtualize visible models
Use `table.getRowModel().rows` for rows and `table.getVisibleLeafColumns()` for columns. Recompute counts when filtering, sorting, expansion, or visibility changes.
### Make CSS geometry agree with measurement
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.
### Fetch before the virtual end
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.
## Common Mistakes
### HIGH Virtualizing raw data
Wrong:
```ts
const rows = data
```
Correct:
```ts
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`
### HIGH Expecting getter options to stay reactive
Wrong:
```ts
const virtualizer = createVirtualizer({
get count() {
return rows.length
},
getScrollElement,
})
```
Correct:
```ts
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`
### HIGH Omitting the geometry contract
Wrong:
```svelte
{#each rowVirtualizer.getVirtualItems() as item}
{rows[item.index].id}
{/each}
```
Correct:
```svelte
```
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`
## API Discovery
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.