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.
64
77%
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
Fix and improve this skill with Tessl
tessl review fix ./packages/lit-table/skills/with-tanstack-virtual/SKILL.mdThis skill builds on @tanstack/table-core#core plus this package's getting-started and table-state skills.
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>`
}
}Set the virtualizer count from table.getRowModel().rows.length; index virtual items into that same array so filtering, sorting, expansion, and pagination are respected.
For column virtualization, estimate from column.getSize() and remeasure when columnSizing changes. Render widths from the same sizing state.
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.
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
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
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
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
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.
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.