getting-started
SkillWeb & browsingGuides your agent through building a working TanStack React Table v9 component with correct hooks and rendering.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the getting-started skill
About this skill
Create and render a TanStack React Table v9 table with useTable, tableFeatures, stable data and columns, row/header models, and table.FlexRender. Load for a first React table, headless rendering, or when v8 useReactTable examples are producing the wrong setup.
What this skill tells your AI
The instructions your AI receives, as published by octanejs/octane in packages/tanstack-table/upstream/skills/getting-started/SKILL.md and read by ahel’s review.
This skill builds on @tanstack/table-core#core and @tanstack/table-core#table-features. Read them first for the headless model and explicit feature registration.
Setup
import { useMemo, useState } from 'react'
import {
createColumnHelper,
tableFeatures,
useTable,
} from '@tanstack/react-table'
type Person = { name: string; age: number }
const features = tableFeatures({})
const helper = createColumnHelper<typeof features, Person>()
const columns = helper.columns([
helper.accessor('name', { header: 'Name' }),
helper.accessor('age', { header: 'Age' }),
])
export function PeopleTable() {
const [data] = useState<Person[]>([{ name: 'Ada', age: 36 }])
const table = useTable({ features, columns, data })
return (
<table>
<thead>
{table.getHeaderGroups().map((group) => (
<tr key={group.id}>
{group.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder ? null : (
<table.FlexRender header={header} />
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getAllCells().map((cell) => (
<td key={cell.id}>
<table.FlexRender cell={cell} />
</td>
))}
</tr>
))}
</tbody>
</table>
)
}
Table produces models and state; React owns the semantic markup, styles, event affordances, and accessibility.
Core Patterns
Add only the feature the table uses
import {
createSortedRowModel,
rowSortingFeature,
tableFeatures,
} from '@tanstack/react-table'
const sortableFeatures = tableFeatures({
rowSortingFeature,
sortedRowModel: createSortedRowModel(),
})
Row-model slots belong inside tableFeatures, after their prerequisite feature.
Keep static inputs outside render
const features = tableFeatures({})
const data: Person[] = [{ name: 'Ada', age: 36 }]
Use state, memoization, or query results for changing data; avoid a new fallback array every render.
Common Mistakes
HIGH Copying the v8 table constructor
Wrong:
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})
Correct:
const table = useTable({ data, columns, features })
V9 uses useTable; optional row models are registered as feature slots rather than table options.
Source: docs/framework/react/guide/migrating.md
HIGH Assuming feature APIs are global
Wrong:
const features = tableFeatures({})
Correct:
const features = tableFeatures({
rowSortingFeature,
sortedRowModel: createSortedRowModel(),
})
Sorting state and methods do not exist until the sorting feature is registered.
Source: packages/table-core/src/TableFeatures.ts
MEDIUM Recreating fallback data each render
Wrong:
const table = useTable({ features, columns, data: response.data ?? [] })
Correct:
// module scope
const EMPTY_DATA: Person[] = []
const table = useTable({ features, columns, data: response.data ?? EMPTY_DATA })
A fresh fallback invalidates data-dependent models on every render.
Source: docs/framework/react/guide/data.md
API Discovery
Inspect node_modules/@tanstack/react-table/dist/index.d.ts first, then the exported useTable.d.ts, FlexRender.d.ts, or core feature source. Use installed declarations so names match the consumer's exact v9 version.
Signals
- GitHub stars
- 1k
- Forks
- 56
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
getting-started-octanejs- Source
- github.com/octanejs/octane