This skill builds on @tanstack/table-core#core and @tanstack/table-core#table-features. Read them first for the headless model and explicit features.
Setup
import { Component, signal } from '@angular/core'
import { FlexRender, injectTable, tableFeatures } from '@tanstack/angular-table'
type Person = { name: string; age: number }
const features = tableFeatures({})
const columns = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'age', header: 'Age' },
]
@Component({
selector: 'app-table',
imports: [FlexRender],
template: `<table>
<tbody>
@for (row of table.getRowModel().rows; track row.id) {
<tr>
@for (cell of row.getAllCells(); track cell.id) {
<td>
<ng-container *flexRenderCell="cell; let value">{{
value
}}</ng-container>
</td>
}
</tr>
}
</tbody>
</table>`,
})
export class TableComponent {
readonly data = signal<Person[]>([{ name: 'Ada', age: 36 }])
readonly table = injectTable(() => ({ features, columns, data: this.data() }))
}
Core Patterns
Keep static inputs outside the initializer
injectTable reruns its options initializer when a signal read changes. Define features, row-model factories, and columns at module or stable class scope; read only changing values inside.
Render each content kind correctly
Import FlexRender for *flexRender, *flexRenderCell, *flexRenderHeader, and *flexRenderFooter. Definitions may yield primitives, TemplateRef, component types, or flexRenderComponent(...); Table does not supply markup or CSS.
Common Mistakes
CRITICAL Calling injectTable outside DI
Wrong:
export function makeTable() {
return injectTable(() => ({ features, columns, data }))
}
Correct:
export class TableComponent {
readonly table = injectTable(() => ({ features, columns, data: this.data() }))
}
injectTable asserts an Angular injection context and registers lifecycle cleanup there.
Source: packages/angular-table/src/injectTable.ts
HIGH Reallocating static options reactively
Wrong:
injectTable(() => ({
features: tableFeatures({}),
columns: makeColumns(),
data: this.data(),
}))
Correct:
const features = tableFeatures({})
const columns = makeColumns()
injectTable(() => ({ features, columns, data: this.data() }))
Every signal change reruns the initializer; rebuilding static inputs invalidates memoized Table work.
Source: packages/angular-table/src/injectTable.ts
HIGH Treating a render function as a component
Wrong:
cell: () => flexRenderComponent(() => 'value')
Correct:
cell: () => 'value'
flexRenderComponent wraps an Angular component type; ordinary functions and primitives are handled directly by FlexRender.
Source: docs/framework/angular/guide/rendering.md
API Discovery
Inspect node_modules/@tanstack/angular-table/dist/types/ for the bundled public API; inspect optional feature APIs in installed @tanstack/table-core/dist/features/.