@tanstack/lit-table
Version:
Headless UI for building powerful tables & datagrids for Lit.
148 lines (110 loc) • 5.06 kB
Markdown
name: with-tanstack-virtual
description: >
Virtualize Lit Table final row or column models with @tanstack/lit-virtual VirtualizerController, host lifecycle-aware counts, scroll refs, stable keys, dynamic measurement, sticky CSS, grid/flex sizing, and infinite data. Load for large rendered tables; Virtual is renderer composition, not tableFeatures.
metadata:
type: composition
library: '@tanstack/lit-table'
framework: lit
library_version: '9.2.4'
requires:
- '@tanstack/table-core#core'
- getting-started
- table-state
sources:
- 'TanStack/table:docs/framework/lit/guide/virtualization.md'
- 'TanStack/table:examples/lit/virtualized-rows'
- 'TanStack/table:examples/lit/virtualized-columns'
- 'TanStack/table:examples/lit/virtualized-infinite-scrolling'
This skill builds on @tanstack/table-core#core plus this package's getting-started and table-state skills.
## Setup
```ts
import { LitElement, html } from 'lit'
import { customElement } from 'lit/decorators.js'
import { createRef, ref } from 'lit/directives/ref.js'
import { VirtualizerController } from '@tanstack/lit-virtual'
import {
TableController,
tableFeatures,
type ColumnDef,
} from '@tanstack/lit-table'
type Item = { id: string; name: string }
const features = tableFeatures({})
const columns: Array<ColumnDef<typeof features, Item>> = [
{ accessorKey: 'name', header: 'Name' },
]
const data: Array<Item> = Array.from({ length: 10_000 }, (_, id) => ({
id: String(id),
name: `Item ${id}`,
}))
@customElement('virtual-items')
export class VirtualItems extends LitElement {
private controller = new TableController<typeof features, Item>(this)
private scroller = createRef<HTMLDivElement>()
private virtualizer = new VirtualizerController(this, {
count: data.length,
getScrollElement: () => this.scroller.value,
estimateSize: () => 32,
overscan: 5,
})
protected render() {
const table = this.controller.table({
features,
columns,
data,
getRowId: (row) => row.id,
})
const rows = table.getRowModel().rows
const v = this.virtualizer.getVirtualizer()
v.setOptions({ ...v.options, count: rows.length })
return html`<div
${ref(this.scroller)}
style="height:400px;overflow:auto;position:relative"
>
<div style="height:${v.getTotalSize()}px;position:relative">
${v
.getVirtualItems()
.map(
(item) =>
html`<div
style="position:absolute;transform:translateY(${item.start}px)"
>
${rows[item.index].getValue('name')}
</div>`,
)}
</div>
</div>`
}
}
```
## Core Patterns
### Virtualize the final row model
Set the virtualizer count from `table.getRowModel().rows.length`; index virtual items into that same array so filtering, sorting, expansion, and pagination are respected.
### Keep geometry sources consistent
For column virtualization, estimate from `column.getSize()` and remeasure when columnSizing changes. Render widths from the same sizing state.
### Treat unsupported combinations as user composition
Drag-and-drop plus virtualization is not a maintained Table recipe. Start from the maintained Virtual example and reconcile both libraries' transforms, measurement, and auto-scroll contracts explicitly.
## Common Mistakes
### HIGH Virtualizing raw data
Wrong: index virtual items into the original `data` array.
Correct: index into `table.getRowModel().rows`.
Raw data ignores active Table processing and produces mismatched rows after sorting or filtering.
Source: TanStack/table:examples/lit/virtualized-rows
### HIGH Count and rows come from different models
Wrong: use `data.length` after filters change but render filtered rows.
Correct: update the virtualizer count from the same current `rows` array being rendered.
Mismatched geometry can index undefined rows or leave blank scroll space.
Source: TanStack/table:examples/lit/virtualized-rows
### HIGH Recreating columns during host renders
Wrong: pass `columns: [...columns]` from `render()` to work around a readonly tuple.
Correct: declare a stable mutable `Array<ColumnDef<typeof features, Item>>` once and pass `columns` directly.
Lit host updates can be driven by scrolling and measurement. A fresh column reference rebuilds Table's column pipeline on every such render.
Source: TanStack/table:examples/lit/virtualized-rows
### HIGH Sticky and sizing CSS assumed automatic
Wrong: add a VirtualizerController and expect sticky headers, widths, and transforms.
Correct: implement the scroll container, total-size spacer, absolute row transforms, sticky regions, and widths in Lit CSS/templates.
Both libraries are headless over rendering geometry.
Source: TanStack/table:docs/framework/lit/guide/virtualization.md
## API Discovery
Inspect `node_modules/@tanstack/lit-table/dist/index.d.ts` and `node_modules/@tanstack/lit-virtual/dist/`. Use maintained examples for geometry; do not register Virtual in `tableFeatures`.