CtrlK
BlogDocsLog inGet started
Tessl Logo

with-tanstack-virtual

Virtualize Angular Table final row or column models inside the correct injection and reactive lifecycle with signal counts, scroll elements, keys, measurement, transforms, sticky regions, grid/flex sizing, and infinite data.

64

Quality

78%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Fix and improve this skill with Tessl

tessl review fix ./packages/angular-table/skills/with-tanstack-virtual/SKILL.md
SKILL.md
Quality
Evals
Security

This skill builds on @tanstack/table-core#core, getting-started, and table-state. Virtual is a rendering concern over final Table models, not a feature plugin.

Setup

import { computed, viewChild } from '@angular/core'
import { injectVirtualizer } from '@tanstack/angular-virtual'
import type { ElementRef } from '@angular/core'

export class VirtualTable {
  readonly scrollElement =
    viewChild<ElementRef<HTMLDivElement>>('scrollElement')
  readonly rows = computed(() => this.table.getRowModel().rows)
  readonly rowVirtualizer = injectVirtualizer(() => ({
    count: this.rows().length,
    scrollElement: this.scrollElement()?.nativeElement,
    estimateSize: () => 34,
    getItemKey: (index) => this.rows()[index]!.id,
    overscan: 5,
  }))
  readonly virtualRows = computed(() => this.rowVirtualizer.getVirtualItems())
  readonly totalSize = computed(() => this.rowVirtualizer.getTotalSize())
}

Core Patterns

Derive reactive models

Use computed(() => table.getRowModel().rows) and computed(() => table.getVisibleLeafColumns()). injectVirtualizer tracks signal reads in its initializer and requires injection context.

Own the layout contract

Give the scroll element bounded overflow, create a total-size spacer, and translate or measure each virtual item. Use grid/flex widths for dynamic rows/columns and keep sticky headers inside the correct scroll geometry.

Gate infinite fetches

When the last virtual item approaches fetched length, fetch only if more server rows exist and a request is not active. Manual sorting requires server-sorted pages and a reset/refetch policy.

Common Mistakes

CRITICAL Constructing outside injection context

Wrong:

export function virtualize(options) {
  return injectVirtualizer(() => options)
}

Correct:

export class VirtualTable {
  readonly virtualizer = injectVirtualizer(() => options())
}

The Angular virtualizer follows DI lifecycle rules just like injectTable.

Source: examples/angular/virtualized-rows/src/app/app.ts

HIGH Virtualizing raw data

Wrong:

readonly rows = computed(() => this.data())

Correct:

readonly rows = computed(() => this.table.getRowModel().rows)

Raw data bypasses Table filtering, sorting, expansion, grouping, and pagination.

Source: docs/framework/angular/guide/virtualization.md

HIGH Forgetting spacer and transforms

Wrong:

@for (item of virtualRows(); track item.key) {
<div>{{ rows()[item.index].id }}</div>
}

Correct:

<div [style.height.px]="totalSize()" style="position:relative">
  <div style="position:absolute"></div>
</div>

Virtual computes ranges and sizes but does not apply DOM geometry, sticky CSS, or column widths.

Source: examples/angular/virtualized-columns/src/app/app.ts

API Discovery

Inspect installed @tanstack/angular-table/dist/types/, installed @tanstack/angular-virtual/dist/, and the maintained Angular examples for current row, column, measurement, and infinite patterns.

Repository
TanStack/table
Last updated
First committed

Is this your skill?

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.