Technical Documentation: TanStack/table
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/alpine-table/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/angular-table/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/ember-table/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/lit-table/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/match-sorter-utils/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/octane-table/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/preact-table-devtools/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/preact-table/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/react-table-devtools/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/react-table/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/solid-table-devtools/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/solid-table/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/svelte-table/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/table-core/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/table-devtools/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/vue-table-devtools/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`packages/vue-table/README.md`)
[Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
TanStack Table
[!NOTE]
You may know TanStack Table by the adapter names:
A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
- Framework-agnostic core with bindings for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine
- 100% customizable — bring your own UI, components, and styles
- Sorting, filtering, grouping, aggregation & row selection
- Lightweight, virtualizable & server‑side friendly
[Read the Docs →](https://tanstack.com/table)
Using an AI Coding Agent?
TanStack Table ships TanStack Intent skills inside each adapter package. After installing the library, run:
npx @tanstack/intent@latest installto add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes intent list to browse available skills and intent load <skill> to print one for inspection. Skills are versioned with the library — your agent gets guidance that matches the version of @tanstack/<framework>-table you installed. Only available for v9 and above.
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config – Tooling for JS/TS packages
- TanStack DB – Reactive sync client store
- TanStack DevTools – Unified devtools panel
- TanStack Form – Type‑safe form state
- TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching
- TanStack Ranger – Range & slider primitives
- TanStack Router – Type‑safe routing, caching & URL state
- TanStack Start – Full‑stack SSR & streaming
- TanStack Store – Reactive data store
- TanStack Virtual – Virtualized rendering
… and more at TanStack.com »
Overview (`examples/alpine/realtime-trading/README.md`)
Alpine realtime trading benchmark
This standalone example exercises the current TanStack Alpine Table adapter
with a high-frequency worker feed, immutable snapshots, interactive columns,
custom quote elements, Virtual Core, and browser diagnostics. It is a
repeatable UI stress workload, not an exchange or network benchmark.
Run and verify
pnpm --dir examples/alpine/realtime-trading devOpen http://localhost:7777.
pnpm --dir examples/alpine/realtime-trading test:types
pnpm --dir examples/alpine/realtime-trading lint
pnpm --dir examples/alpine/realtime-trading build
pnpm --dir examples/alpine/realtime-trading test:e2eUse a production build for performance recordings.
Structure and ownership
| Path | Responsibility |
|---|---|
src/feed/ |
Market model, instrument universe, feed config, immutable update helpers, and direct TanStack atom controller. |
src/feed/worker/ |
Typed protocol, deterministic engine, and module worker. |
src/benchmark/ |
Browser monitor and benchmark controller. |
src/shell/configurator-options.ts |
Declarative select/range options used by the sidebar. |
src/table/table-config/ |
Grouped columns and custom quote elements. |
src/table/ |
Shared interaction helpers and virtualization constants. |
src/main.ts |
Alpine component factory and the imperative bridges to Store, Table, Virtual Core, observers, and DOM layout. |
index.html |
Declarative full-viewport shell, table template, controls, diagnostics, and status bar. |
Alpine keeps the markup declarative in index.html; tradingApp owns only the
runtime coordination that cannot live in HTML. Feed and benchmark remain
separate controllers. Direct feed atoms are bridged into an Alpine reactive view
object individually: the high-frequency quotes atom is independent from
status and configuration atoms. Selected symbol and renderer mode are also
dedicated atoms. Every subscription, observer, virtualizer mount, controller,
and worker has explicit cleanup registered by init()/destroy().
Feed and worker pipeline
Defaults are 100 instruments, 10K generated samples/s, 20 ms delivery, enabled
intraday charts, and 16 ms chart sampling.
Mutable quotes stay in the worker. A deterministic 16 ms budget loop generates
samples, a row-indexed Map coalesces repeated instrument changes, and a
separate timer publishes the latest unique rows. The main thread creates a new
outer array and only new changed rows. Unchanged rows and unsampled history
arrays retain identity. Session IDs reject late messages after resets or row
count changes.
- Synthetic quote workload is generated worker samples/s, not Alpine DOM
updates or worker messages. - Worker delivery interval controls coalesced message cadence; 20 ms targets
about 50 messages/s. - Row updates counts unique immutable row objects applied.
- Message samples is the generated work represented by the latest batch.
The 25K burst intentionally creates and flushes one expensive message.
Worker delivery resembles an upstream stream but does not include a network.
Alpine table architecture
The table has 14 leaf columns grouped into Instrument, Price & Change, Order
Book, Session, and Chart. It supports sorting/filtering, on-change resizing,
double-click reset, drag column ordering, CSS hover, row selection, drag cell
ranges, keyboard navigation, and Price/Move/Percent/Sparkline custom elements.
Stable instrument IDs back getRowId. The local rows() function caches the
row model by feed-array identity and table-state version so repeated Alpine
template reads do not call getRowModel() again during the same state. OneTradingGridPointerController handles all body input using composedPath() and
data attributes instead of per-cell listeners.
Column widths are CSS custom properties updated only by sizing/order
subscriptions. ResizeObserver performs initial fit and stops after manual
resize. Component A/B swapping is an explicit lifecycle stress mode; stable
rendering is the realistic default.
Virtualization
- Below 200 rows, automatic mode chooses Full DOM, but Virtual remains
selectable. - From 200 through 1,499 rows, automatic mode chooses TanStack Virtual and Full
DOM remains selectable. - At 1,500 rows or more, Virtual is forced and the control is locked.
main.ts owns one @tanstack/virtual-core instance, updates its count/options,
and increments a narrow Alpine virtual version when its range changes. It uses
32 px estimates, 10-row overscan, row IDs as keys, transformed rows, and a body
spacer. The footer reads the instance range. Both modes applycontent-visibility: auto; Full DOM still creates all rows/cells.
Performance decisions
- worker generation and coalescing before main-thread delivery;
- structural sharing for unchanged rows and histories;
- cached row-model reads keyed by actual dependencies;
- stable table/virtual row IDs;
- dedicated reactive versions for table and virtualizer changes;
- one delegated grid interaction controller and CSS hover;
- CSS variables for column sizes;
- configurable chart cadence and opt-in component churn;
- virtual mounting for large row sets;
- explicit lifecycle cleanup and low-frequency metrics publication.
The immutable outer array changes by design. Stable inner references reduce
cell work, but sorting/filtering can still require a fresh row-model pass.
Diagnostics and interpretation
The sidebar keeps four cross-framework health signals prominent: estimated
frame callbacks, average snapshot-to-DOM-commit latency, cumulative long
animation frames, and changed-row/snapshot throughput. The remaining counters
stay in Diagnostics so they do not look like equally important scores.
AVG COMMIT is not the duration of an Alpine render function and it does not
include the browser's later layout or paint. It starts when a new immutable
snapshot is applied and ends in one coalesced Alpine.nextTick() callback,
after Alpine has flushed the corresponding DOM work. The average uses a rolling
3-second window; diagnostic p95/max use 10 seconds. If several worker
messages arrive before one Alpine flush, they intentionally produce one commit
sample from the earliest pending snapshot.
The frame-rate estimate counts standard requestAnimationFrame callbacks over
one second; it is refresh-rate dependent and is not GPU/compositor FPS.Observed MutationRecords/s is observer delivery count, not browser DOM
operations. It tracks text, child-list, class, and style changes while excluding
selection data attributes to reduce noise. The observer itself still adds work
at very high mutation rates. Heap is Chrome-only, current and GC-sensitive;
temporary growth is not proof of a leak without post-GC retention. React Scan is
not part of these shared measurements. User Timing commit and row-model entries
are sampled once every 20 candidates; the numeric counters remain exact, while
the Performance timeline avoids one retained entry per hot-path execution.
Standalone policy
This folder intentionally contains its own instruments, feed, worker,
benchmark, shell markup, styles, and table code. It can run independently or be
copied to StackBlitz, so common source and README sections are duplicated across
adapters by design.
The workspace resolves the pinned @tanstack/alpine-table dependency to the
local adapter package while keeping the manifest release-like.
Overview (`examples/angular/realtime-trading/README.md`)
Angular realtime trading benchmark
This is a standalone stress example for the current TanStack Angular Table
adapter. It combines a high-frequency synthetic market feed, immutable row
snapshots, sortable and resizable columns, range selection, dynamic Angular
cell components, optional row virtualization, and browser performance
instrumentation.
The goal is not to reproduce an exchange. It is to provide a repeatable grid
workload in which feed generation, message delivery, state application, table
work, Angular rendering, and browser layout can be measured separately.
Run and verify
pnpm --dir examples/angular/realtime-trading devOpen http://localhost:7777.
Use a production build before recording representative timings:
pnpm --dir examples/angular/realtime-trading build
pnpm --dir examples/angular/realtime-trading test:types
pnpm --dir examples/angular/realtime-trading lint
pnpm --dir examples/angular/realtime-trading test:e2eDevelopment mode includes Angular assertions and extra benchmark bookkeeping,
so its absolute timings should not be compared with a production build.
Directory structure
| Path | Responsibility |
|---|---|
src/app/feed/ |
Market types, the instrument universe, feed configuration, immutable update helpers, and the Angular feed service. |
src/app/feed/worker/ |
Worker protocol, deterministic market engine, and the module Web Worker that generates and publishes quote updates. |
src/app/benchmark/ |
Performance monitor and the controller that connects feed lifecycle events to UI diagnostics. |
src/app/shell/ |
Full-viewport layout, header, metrics strip, sidebar configurator, selected-instrument panel, diagnostics, and status bar. |
src/app/table/table-config/ |
Grouped column definitions and custom quote cell components. |
src/app/table/view/ |
Header component plus table-level selection and cell host directives. |
src/app/table/ |
Current table, table interactions, initial column fitting, and the TanStack Virtual integration. |
src/app/table/worker/ |
Optional experimental table-worker implementation and its row-model worker. |
src/app/app.ts |
Composition root; it projects only the selected table implementation into the shell. |
The feed, benchmark, shell, and table layers are deliberately separate. The
feed can run without the benchmark monitor, and shell components inject only
the controller or service whose state they display.
Data and worker pipeline
The initial configuration is 100 instruments, 10K generated samples per
second, a 20 ms delivery interval, enabled intraday charts, and 16 ms intraday
sampling.
MarketFeedServicecreates a module worker and sends the current
configuration.market-feed-engine.tsowns mutable quote state inside the worker. A
deterministic PRNG and stable instrument IDs keep runs reproducible.- A 16 ms generation loop accrues a fractional sample budget and applies the
requested synthetic workload to the worker-owned quotes. - Updated instruments are stored in a
Mapby row index. Repeated updates to
one instrument are coalesced, retaining its latest value. - A separate publication timer posts one message at the configured delivery
interval. Generation rate and message rate are therefore independent. - The main thread creates a new outer quotes array and replaces only the row
objects included in the message. Unchanged rows retain their references. - A session ID rejects messages from an obsolete worker configuration after a
reset or instrument-count change.
The terms in the UI are intentionally distinct:
- Synthetic quote workload is generated samples per second inside the
worker. It is not DOM events, table renders, orpostMessagecalls. - Worker delivery interval is the target cadence for coalesced messages.
For example, 20 ms targets roughly 50 messages per second. - Row updates is the number of unique immutable row objects applied on the
main thread. It can be lower than generated samples because of coalescing. - Message samples is the number of generated samples represented by the
latest delivered batch.
Intraday history is sampled independently of quote generation. Its interval
controls how frequently a row receives a new history array; other quote fields
can continue changing between history samples. The 25K burst command generates
and flushes one intentionally expensive batch immediately.
The worker is analogous to an upstream WebSocket/SSE producer, but it is not a
network benchmark: serialization, browser worker messaging, main-thread state
application, and rendering are in scope; network latency is not.
Angular state and rendering architecture
MarketFeedServiceowns feed signals and worker lifecycle.TradingBenchmarkControllerowns benchmark-only state, selected symbol,
renderer mode, virtualization preference, and derived metrics.- The application is zoneless and uses signal inputs, outputs, queries,
computed,effect,DestroyRef, andOnPushcomponents. - The root component does not subscribe to individual diagnostics. It only
chooses the normal or experimental worker-backed table and supplies the same
signal values to it. - Custom cells are declared with
flexRenderComponentonly where an Angular
component is required. Plain columns remain plain render callbacks. - Stable row IDs come from the instrument ID, so sorting and updates do not
make row identity depend on the current array position.
The optional table worker is separate from the market-feed worker. The feed
worker always produces market data; the table worker moves the supported
row-model work off the main thread. Both table paths intentionally have their
own column configuration boundary so component render tokens stay explicit.
Table behavior
The grid has 14 leaf columns grouped into Instrument, Price & Change, Order
Book, Session, and Chart sections. It supports:
- core sorting and filtering;
- on-change column resizing, including double-click reset;
- drag-and-drop leaf-column ordering with a visible drop target;
- CSS-only row hover;
- click/modified-click row selection;
- mouse-drag cell range selection and keyboard navigation;
- custom Price, Move, Percent Change, and Sparkline components;
- a stress mode that swaps the move component type when direction changes,
intentionally exercising destruction and recreation.
Selection events are delegated once at the table boundary byTradingGridSelectionDirective. It resolves a cell from event.composedPath()
and data attributes, avoiding a mousedown/move listener per cell. The cell
directive uses signal inputs and host properties for width, focus, ARIA, and
selection-edge attributes; it does not toggle CSS classes imperatively.
Column sizes are written to table-level CSS custom properties. Cells reference
those variables rather than recomputing an inline pixel width during every
quote update. A ResizeObserver expands the initial column sizes to available
width; manual resizing disables subsequent automatic fitting.
Full DOM and virtual rows
The internal preference is auto, tanstack, or none:
- Below 200 rows,
autoresolves to Full DOM, but virtualization remains
manually selectable. - From 200 through 1,499 rows,
autoresolves to TanStack Virtual, and the
user may still switch back to Full DOM. - At 1,500 rows or more, virtualization is forced and the control is locked to
prevent an accidental full-table mount.
TanStack Virtual uses a fixed 32 px estimate, 10-row overscan, instrument row
IDs as item keys, cached measurements, and transformed rows inside a body-sized
spacer. Only the visible overscan window creates Angular row views. The footer
reads the virtualizer range and reports the current row interval.
The Angular virtualizer disables application-wide ticks and schedules a localdetectChanges() flush for virtualizer changes. This confines scroll-driven
updates to the table instead of checking the complete application shell.
Both Full DOM and virtual rows use content-visibility: auto with a matching
intrinsic block size. In Full DOM this may save browser style/layout/paint work,
but Angular still creates every row and cell. In virtual mode it is an
additional browser hint; virtualization remains the mechanism that limits
mounted views.
Performance decisions
- Market calculations execute off the main thread.
- Repeated worker samples are coalesced before crossing the worker boundary.
- Immutable snapshots preserve unchanged row and history references.
- Stable row IDs and keyed
@forblocks preserve row identity. - High-frequency selection input is delegated at the table, not bound per
cell. - Column widths use CSS variables and update only on sizing/order changes.
- Shell widgets read narrow signal state and do not make the root consume the
complete feed. - Dynamic component churn is opt-in; the stable renderer is the realistic
baseline. - Virtualization limits framework and DOM work for larger data sets.
- Benchmark metrics publish at a lower cadence than the data feed.
content-visibility, worker generation, and virtualization solve different
problems. None of them prevents the table core from rebuilding a sorted or
filtered row model when its data/state inputs require it.
Diagnostics
The benchmark layer observes the feed through callbacks, so it can be removed
without changing feed semantics. It reports:
- a compact Live health summary in the configurator: estimated frame
callbacks over a rolling 1-second window, average market-mutation-to-DOM-
commit latency over 3 seconds, cumulative long animation frames, and
throughput as changed rows plus applied snapshots per second; - generated worker samples, changed rows, received messages, state applies,
and completed DOM commits; - p95/maximum commit latency over a rolling 10-second window and commits over
16.7 ms accumulated since reset; - long animation frames and worst duration when the browser supports the Long
Animation Frames API; - mounted cell hosts and live/created/destroyed dynamic components;
- cell-render callbacks by column and component executions by type;
- DOM
MutationRecorddelivery rate from aMutationObserverlimited to text,
child-list, andclass/styleattributes; - core row-model call rate and average/maximum duration;
- heap information where the browser exposes it.
The frame-rate value is an estimated requestAnimationFrame callback rate, not
GPU-presented FPS; its healthy ceiling follows the display refresh rate. The
latency starts at the first pending feed mutation and ends in Angular'safterEveryRender callback, after Angular has committed the DOM.
Changed rows/s sums the update array lengths delivered by each snapshot.
Symbols are deduplicated inside one worker message, but the same row can count
again in the next snapshot; it is therefore applied row throughput, not the
number of distinct instruments touched during the entire second.
Renderer callback counts are not DOM mutation counts. A callback can execute
and still reuse a component/view, while a component execution count records the
framework component path itself. MutationObserver reports delivered records,
not a one-to-one count of browser DOM operations; record coalescing and custom
component internals affect it, and observing a hot subtree has profiling
overhead. The attribute filter excludes selection-oriented data-* changes to
reduce noise. Heap is Chromium-only and GC-sensitive. Growth during swap mode is
not automatically a leak: confirm retention with repeated post-GC snapshots.
The rAF loop only appends a timestamp; rolling aggregation and heap reads run at
the 500 ms metrics publication cadence. Mutation observation remains the most
intrusive diagnostic because the browser must create records for the observed
subtree.
Use Chrome Performance/Angular DevTools for call stacks and frame analysis, and
the in-app diagnostics for controlled comparisons. Keep row count, workload,
delivery interval, renderer mode, and virtual mode identical between runs.
Standalone example policy
This folder intentionally contains its own instruments, feed engine, worker,
benchmark monitor, styles, and UI instead of importing a shared demo package.
That duplication lets the example run independently and be copied to
StackBlitz. Shared concepts and some README text are therefore repeated across
adapter examples by design.
The package pins @tanstack/angular-table to the repository version. The root
workspace override resolves it to packages/angular-table while retaining a
release-like example manifest.
Overview (`examples/ember/realtime-trading/README.md`)
Ember realtime trading benchmark
This standalone example exercises the current TanStack Ember Table adapter
with a high-frequency worker feed, immutable snapshots, interactive columns,
Glimmer quote components, Virtual Core, and browser diagnostics. It is a
repeatable rendering stress workload, not an exchange or network benchmark.
Run and verify
pnpm --dir examples/ember/realtime-trading devOpen http://localhost:7777.
pnpm --dir examples/ember/realtime-trading test:types
pnpm --dir examples/ember/realtime-trading build
pnpm --dir examples/ember/realtime-trading test:e2eThis example currently has no separate lint package script. Use a production
build for performance recordings.
Structure and ownership
| Path | Responsibility |
|---|---|
app/feed/ |
Market model, instrument universe, configuration, immutable update helpers, and Ember-native tracked controller. |
app/feed/worker/ |
Typed protocol, deterministic engine, and module worker. |
app/benchmark/ |
Browser monitor and benchmark controller. |
app/components/shell/ |
GTS header, metrics, configurator, diagnostics, selected instrument, status bar, and shell layout. |
app/components/table/ |
Trading table Glimmer component. |
app/table/table-config/ |
Grouped columns and custom GTS quote components. |
app/table/ |
Table construction, features, delegated interactions, and Virtual Core integration. |
app/utils/subscriptions.ts |
Owner-bound destruction cleanup helper. |
app/templates/application.gts |
Creates feed/benchmark controllers and composes the shell. |
MarketFeedController owns worker/feed state. TradingBenchmarkController
observes feed lifecycle callbacks and owns diagnostic/view state. The
application passes stable controllers to shell/table components; Glimmer
components read the controller's @tracked properties directly. quotes is
its own high-frequency tracked property; feed status and each configuration
value are separate tracked properties. Derived benchmark values use @cached.
There is no foreign Store subscription or mirrored component state, and the
root does not proxy every quote or metric as application state.
Feed and worker pipeline
Defaults are 100 instruments, 10K generated samples/s, 20 ms delivery, enabled
intraday charts, and 16 ms chart sampling.
Mutable quote state is private to the worker. A deterministic 16 ms budget loop
generates samples, a row-indexed Map coalesces repeated changes, and a separate
timer publishes the latest unique rows. The main thread creates a new outer
array and replaces only changed rows; untouched rows and unsampled history
arrays retain references. Session IDs reject stale messages after resets or
instrument-count changes.
- Synthetic quote workload is worker-generated samples/s, not Glimmer
renders, events, or messages. - Worker delivery interval controls coalesced message cadence; 20 ms targets
around 50 messages/s. - Row updates is the number of unique immutable rows applied.
- Message samples is the generated work represented by the latest message.
Intraday history uses its own cadence. The 25K burst deliberately publishes one
heavy batch. The worker resembles an upstream stream, without network latency.
Ember table architecture
The grid has 14 leaf columns grouped into Instrument, Price & Change, Order
Book, Session, and Chart. It includes sorting/filtering, on-change resizing,
double-click reset, drag ordering, CSS hover, row selection, drag cell ranges,
keyboard navigation, and Price/Move/Percent/Sparkline Glimmer components.
flexRenderComponent is used for actual component cells. Stable instrument IDs
back row identity. One table/body interaction path resolves cells throughevent.composedPath() and data attributes instead of allocating handlers on
every cell. Column widths are CSS variables updated on sizing/order; aResizeObserver performs initial fitting until manual resize. The
CSS-variable string is an @cached getter that reads Ember-reactive table
sizing and order, so the first fit and later table updates reach the DOM
without an imperative Store subscription. A/B move component swapping is an
explicit lifecycle stress mode.
Virtualization
- Below 200 rows, automatic mode uses Full DOM, while Virtual is selectable.
- From 200 through 1,499 rows, automatic mode uses TanStack Virtual and Full DOM
remains selectable. - At 1,500 rows or more, Virtual is forced and its control is disabled.
The local Virtual Core integration owns one instance with 32 px estimates,
10-row overscan, stable row IDs, transformed rows, and a spacer body. Instance
notifications invalidate only the table range, and the footer reads that range.
Both Full DOM and virtual rows use content-visibility: auto; Full DOM still
creates every Glimmer row/cell.
Performance decisions
- worker-side generation and pre-message coalescing;
- immutable structural sharing for rows/history;
- direct
@trackedfeed/view state and@cachedderived state; - stable table and virtual row identity;
- componentized GTS shell/table/cell boundaries;
- delegated pointer input and CSS hover;
- CSS variables for width propagation;
- opt-in lifecycle churn and independently sampled charts;
- virtual mounting for larger row counts;
- explicit destructor cleanup and lower-frequency metric publication.
The outer array intentionally changes for an immutable batch. Structural
sharing reduces cell work, but sorting/filtering can still trigger row-model
processing when data changes.
Diagnostics and interpretation
The compact Live health section lives in the configurator and reports the
estimated frame callback rate over 1 second, average market-mutation-to-DOM-
commit latency over 3 seconds, cumulative long animation frames, and throughput
as changed rows plus applied snapshots per second. Detailed diagnostics retain
worker samples/messages, state applies, DOM commits, rolling 10-second p95/max
commit latency, cumulative slow commits, mounted hosts, component
lifecycle/execution, row-model timing, DOM mutation records, and heap.
The frame value counts requestAnimationFrame callbacks, not GPU-presented
frames, so its ceiling follows the display refresh rate. Renderer callbacks and
DOM mutations are different measurements. MutationObserver counts delivered
records, not individual DOM operations, and has overhead on a hot subtree; it
only observes text, child-list, and class/style changes to reduce selection
noise. Heap is a Chromium-only, GC-sensitive point-in-time value. User Timing
timeline measures are sampled 1-in-20 while the in-memory latency calculation
keeps every commit, reducing profiler self-interference. Temporary heap growth
during component swapping is not a leak without post-GC retention.
The rAF loop only appends a timestamp; rolling aggregation and heap reads run at
the 500 ms metrics publication cadence. Mutation observation remains the most
intrusive diagnostic because the browser must create records for the observed
subtree.
Changed rows/s sums the update array lengths delivered by each snapshot.
Symbols are deduplicated inside one message, but the same row can count again in
the next snapshot; it is applied row throughput, not distinct instruments per
second.
Standalone policy
All instruments, feed, worker, benchmark, shell, styles, and table code are
copied into this directory intentionally. It remains independently runnable and
StackBlitz-friendly, so common implementation and README material is repeated
across adapters by design.
The workspace resolves @tanstack/ember-table to the repository adapter while
the example package remains release-like.
Overview (`examples/lit/realtime-trading/README.md`)
Lit realtime trading benchmark
This standalone example exercises the current TanStack Lit Table adapter with
a high-frequency worker feed, immutable snapshots, interactive columns, custom
elements for quote cells, Lit Virtual, and browser diagnostics. It is a
repeatable rendering workload, not an exchange or network benchmark.
Run and verify
pnpm --dir examples/lit/realtime-trading devOpen http://localhost:7777.
pnpm --dir examples/lit/realtime-trading test:types
pnpm --dir examples/lit/realtime-trading lint
pnpm --dir examples/lit/realtime-trading build
pnpm --dir examples/lit/realtime-trading test:e2eUse the production build for measurements.
Structure and ownership
| Path | Responsibility |
|---|---|
src/feed/ |
Market types, instruments, configuration, immutable updates, and direct TanStack atom feed controller. |
src/feed/worker/ |
Protocol, deterministic engine, and module worker. |
src/benchmark/ |
Browser monitor, benchmark controller, table timing, and row-model diagnostics. |
src/shell/ |
Custom elements for header, metrics, controls, diagnostics, selected instrument, status, and layout. |
src/shell/controller-element.ts |
Base element that subscribes to controller sources and releases subscriptions on disconnect. |
src/table/table-config/ |
Grouped columns and custom quote cell elements. |
src/table/ |
Lit Table element, delegated interactions, column layout, and Lit Virtual integration. |
src/main.ts |
Root custom element that creates, starts, stops, and passes the controllers. |
The feed controller is independent from the benchmark controller. The root
passes stable controller objects; each shell/table custom element observes the
specific atom/store sources it consumes. Quotes and every feed control/status
value are independent direct atoms; the table observes only quotes andinstrumentCount. ControllerElement converts those
notifications into requestUpdate() and guarantees unsubscribe on disconnect,
avoiding one broad application-level subscription.
Feed and worker pipeline
Defaults are 100 instruments, 10K generated samples/s, 20 ms delivery, enabled
intraday charts, and 16 ms chart sampling.
Mutable market state stays inside the worker. A deterministic 16 ms budget loop
generates samples, a row-indexed Map coalesces repeated instrument changes,
and an independent timer publishes the latest unique rows. The main thread
creates a new outer array and only new changed rows; untouched rows and
unsampled histories retain identity. Session IDs reject obsolete batches after
reset or instrument-count changes.
- Synthetic quote workload is worker-generated samples/s, not custom-element
updates or messages. - Worker delivery interval controls coalesced
postMessagecadence; 20 ms
targets about 50 messages/s. - Row updates is the count of unique immutable rows applied.
- Message samples is generated work represented by the latest batch.
Intraday sampling is independent. The 25K burst deliberately publishes one
heavy batch. Worker messaging resembles an upstream stream without including
network latency.
Lit table architecture
The 14 leaf columns are grouped into Instrument, Price & Change, Order Book,
Session, and Chart. The grid supports sorting/filtering, on-change resizing,
double-click reset, drag column ordering, CSS hover, row selection, drag cell
ranges, keyboard navigation, and Price/Move/Percent/Sparkline custom elements.
Stable instrument IDs define row identity. One body-level interaction
controller resolves cells from event.composedPath() and data attributes,
avoiding handlers on every cell. Column widths are CSS variables written only
when sizing/order changes; a ResizeObserver performs initial fitting until a
manual resize. The A/B move option is intentionally a custom-element lifecycle
stress path.
Virtualization
- Below 200 rows, automatic mode uses Full DOM, with Virtual still selectable.
- From 200 through 1,499 rows, automatic mode uses TanStack Virtual, with Full
DOM still selectable. - At 1,500 rows or more, Virtual is forced and its control is disabled.
Lit Virtual uses a 32 px estimate, 10-row overscan, stable row IDs, transformed
rows, and a spacer body. The footer derives the visible interval from the
virtualizer. Both modes apply content-visibility: auto; Full DOM still creates
all elements even when the browser skips some offscreen layout/paint.
Performance decisions
- worker-side generation and coalescing;
- structural sharing for unchanged rows/history arrays;
- source-specific atom/store subscriptions in custom elements;
- stable keyed row/virtual identity;
- lifecycle-safe controller element subscriptions;
- delegated pointer input and CSS hover;
- CSS variables for width propagation;
- independently configurable chart/component stress;
- virtual mounting at larger sizes;
- metrics published below feed frequency.
A new outer array is required by the immutable contract. Stable row references
help rendering, but sorting/filtering may still recompute the row model when
data changes.
Diagnostics and interpretation
The sidebar keeps four cross-framework health signals prominent: estimated
frame callbacks, average snapshot-to-DOM-commit latency, cumulative long
animation frames, and changed-row/snapshot throughput. The remaining counters
stay in Diagnostics so they do not look like equally important scores.
AVG COMMIT is not the duration of render() and it does not include the
browser's later layout or paint. It starts when a new immutable snapshot is
applied and ends in a guarded microtask queued from updated(). That extra
microtask lets nested quote-cell custom elements finish their Lit updates
before the sample closes. The average uses a rolling 3-second window; diagnostic
p95/max use 10 seconds. The frame-rate estimate counts standardrequestAnimationFrame callbacks over one second. It is refresh-rate dependent
and is not a GPU/compositor FPS measurement.
Callback counts and DOM mutations describe different layers.Observed MutationRecords/s is the number delivered by a MutationObserver
on the table body, not the number of browser DOM operations. The observer tracks
text, child-list, class, and style changes; it excludes selection data
attributes to reduce noise. Registering an observer still adds work at very high
mutation rates, so confirm close results with a Performance recording without
treating this counter as a score. Heap is Chrome-only, current and GC-sensitive;
growth is not a leak without post-GC retention. React Scan is not part of these
shared measurements. User Timing commit and row-model entries are sampled once
every 20 candidates; the numeric counters remain exact, while the Performance
timeline avoids one retained entry per hot-path execution.
Standalone policy
All instruments, feed, worker, benchmark, shell, styles, and table code live in
this folder intentionally. It runs independently and can be copied to
StackBlitz, so shared implementation and README text are duplicated by design.
The workspace resolves the pinned @tanstack/lit-table dependency to the local
adapter package while keeping the manifest release-like.
Overview (`examples/octane/realtime-trading/README.md`)
Octane realtime trading benchmark
This standalone example exercises the current TanStack Octane Table adapter
with a high-frequency worker feed, immutable snapshots, interactive columns,
custom TSRX cells, Virtual Core, and browser diagnostics. It is a repeatable UI
stress workload rather than an exchange/network simulator.
Run and verify
pnpm --dir examples/octane/realtime-trading devOpen http://localhost:7777.
pnpm --dir examples/octane/realtime-trading test:types
pnpm --dir examples/octane/realtime-trading lint
pnpm --dir examples/octane/realtime-trading build
pnpm --dir examples/octane/realtime-trading test:e2eUse a production build for representative measurements.
Structure and ownership
| Path | Responsibility |
|---|---|
src/feed/ |
Market model, instruments, feed config, immutable update helpers, and direct TanStack atom feed controller. |
src/feed/worker/ |
Typed protocol, deterministic engine, and module worker. |
src/benchmark/ |
Browser monitor, benchmark controller, table observer, and row-model timing. |
src/shell/ |
Separate TSRX header, metrics, configurator, diagnostics, selected-instrument, status, and shell components. |
src/table/table-config/ |
Grouped columns and custom TSRX quote components. |
src/table/ |
Octane Table view/setup, interactions, pointer hook, column layout, and Virtual Core hook. |
src/main.tsrx |
Creates controllers and renders the shell composition root. |
MarketFeedController owns feed/worker state. TradingBenchmarkController
observes it and owns diagnostic/view state. The shell receives stable controller
objects; individual components call useSelector from @tanstack/octane-store for
the values they need. Quotes, feed status, row count, workload, delivery, and
chart controls are direct independent atoms. The table subscribes to quotes
and instrumentCount, so a configurator/status update cannot invalidate table
data. Benchmark metrics remain an aggregate snapshot store.
Feed and worker pipeline
Defaults are 100 instruments, 10K generated samples/s, 20 ms delivery, enabled
intraday charts, and 16 ms chart sampling.
The worker keeps mutable quote state private. A deterministic 16 ms budget loop
generates samples, and a row-indexed Map coalesces repeated updates. A separate
publication timer posts the latest unique rows. The main thread creates a new
outer array and replaces only changed row objects; untouched rows and unsampled
history arrays retain their references. Session IDs discard stale batches after
reset or instrument-count changes.
- Synthetic quote workload means generated worker samples/s, not messages,
events, or component renders. - Worker delivery interval controls coalesced message cadence; 20 ms targets
about 50 messages/s. - Row updates counts unique immutable rows applied on the main thread.
- Message samples is the generated work represented by the latest message.
The 25K burst immediately generates and flushes one deliberately heavy batch.
The worker approximates an upstream stream but does not measure network delay.
Octane table architecture
The table has 14 leaf columns grouped into Instrument, Price & Change, Order
Book, Session, and Chart. Sorting/filtering, on-change resizing, double-click
reset, drag ordering, CSS row hover, row selection, drag cell ranges, keyboard
navigation, and custom Price/Move/Percent/Sparkline components are included.
Stable instrument IDs back getRowId. Table, row, header, and quote component
boundaries keep ownership explicit. A local hook delegates pointer handling at
the body and stores transient drag state without publishing render state. It
resolves a cell through event.composedPath() and data attributes rather than
installing per-cell listeners.
Column widths are table CSS variables updated only for sizing/order. AResizeObserver performs the initial fit and stops auto-fitting after manual
resize. The move A/B option deliberately changes component type to measure
creation/destruction; stable mode is the realistic baseline.
Virtualization
- Below 200 rows, automatic mode resolves to Full DOM, while Virtual is
selectable. - From 200 through 1,499 rows, automatic mode resolves to TanStack Virtual and
Full DOM remains selectable. - At 1,500 rows or more, Virtual is forced and the control is locked.
The local useVirtualizer bridge owns one @tanstack/virtual-core instance.
It updates options in place, mounts/unmounts through Octane layout effects, and
increments a local render version on virtualizer changes. Configuration uses a
32 px estimate, 10-row overscan, row IDs as keys, transformed rows, and a spacer
body. The footer reads the Virtualizer range.
Both paths use content-visibility: auto. This can reduce offscreen browser
work in Full DOM but does not prevent Octane from creating every row/cell.
Performance decisions
- worker generation and coalescing before main-thread delivery;
- immutable structural sharing for rows and chart histories;
- selector-based subscriptions with explicit equality;
- stable table and virtual row identity;
- componentized shell/table/cells with local state ownership;
- delegated pointer input and CSS hover;
- CSS variables for column sizes;
- opt-in component churn and independently sampled charts;
- virtual mounting for larger data sets;
- benchmark publication slower than feed updates.
The outer array intentionally changes for immutable publication. Structural
sharing reduces renderer work, but sorted/filtered row models may still execute
when the data input changes.
Diagnostics and interpretation
The compact Live health section lives in the configurator and reports the
estimated frame callback rate over 1 second, average market-mutation-to-DOM-
commit latency over 3 seconds, cumulative long animation frames, and throughput
as changed rows plus applied snapshots per second. Detailed diagnostics retain
worker samples/messages, state applies, DOM commits, rolling 10-second p95/max
commit latency, cumulative slow commits, mounted cells, component
lifecycle/execution, row-model timing, DOM mutation records, and heap.
Octane's completed-render measurement is connected to the feed mutation and
the table's committed layout lifecycle; it should be compared using the same
build and settings. The frame value counts requestAnimationFrame callbacks,
not GPU-presented frames. MutationObserver reports delivered records, not
individual DOM operations, and adds overhead on a hot subtree; it is limited to
text, child-list, and class/style changes. Heap is Chromium-only and
GC-sensitive. User Timing timeline measures are sampled 1-in-20 while the
in-memory latency calculation keeps every commit. Callback execution does not
equal DOM mutation, and heap growth is not a confirmed leak without post-GC
retention.
The rAF loop only appends a timestamp; rolling aggregation and heap reads run at
the 500 ms metrics publication cadence. Mutation observation remains the most
intrusive diagnostic because the browser must create records for the observed
subtree.
Changed rows/s sums the update array lengths delivered by each snapshot.
Symbols are deduplicated inside one message, but the same row can count again in
the next snapshot; it is applied row throughput, not distinct instruments per
second.
Standalone policy
This folder owns copies of its feed, worker, instruments, benchmark, shell,
styles, and table implementation. That duplication keeps it independently
runnable and StackBlitz-friendly; shared README explanations are repeated
across adapters intentionally.
The workspace resolves @tanstack/octane-table to the repository adapter while
the example manifest remains release-like.
Overview (`examples/preact/realtime-trading/README.md`)
Preact realtime trading benchmark
This standalone example exercises the current TanStack Preact Table adapter
with an immutable high-frequency feed, table interactions, custom components,
virtual rows, and browser diagnostics. It is a rendering stress lab rather than
an exchange or network-latency simulation.
Run and verify
pnpm --dir examples/preact/realtime-trading devOpen http://localhost:7777.
pnpm --dir examples/preact/realtime-trading test:types
pnpm --dir examples/preact/realtime-trading lint
pnpm --dir examples/preact/realtime-trading build
pnpm --dir examples/preact/realtime-trading test:e2eUse the production build for measurements; development checks inflate the
absolute cost.
Structure and ownership
| Path | Responsibility |
|---|---|
src/feed/ |
Market types, instruments, immutable update helpers, config, controller, and controller hook. |
src/feed/worker/ |
Worker protocol, deterministic market engine, and module worker. |
src/benchmark/ |
Feed/render timing, DOM/long-frame observers, row-model and component diagnostics. |
src/shell/ |
TanStack Store contexts, viewport shell, metrics, controls, selected instrument, and diagnostics. |
src/table/table-config/ |
Grouped columns, row/cell components, and render counters. |
src/table/ |
Preact Table instance, subscriptions, interactions, column layout, and Virtual Core hook. |
src/App.tsx |
Creates the controllers and composes providers, shell, and table. |
Feed state and benchmark state are different controllers. The feed does not
depend on the monitor; the benchmark subscribes to feed lifecycle callbacks.
TanStack Preact Store contexts pass stable controller objects, while components
subscribe to direct feed atoms. quotes has an independent high-frequency atom;
status, instrument count, workload, delivery, and chart settings each have their
own atom and cannot notify the table data boundary. Renderer mode and selected
symbol are also separate atoms so their updates do not invalidate unrelated
shell content. Benchmark metrics remain an aggregate snapshot store.
Feed and worker pipeline
The initial setup is 100 instruments, 10K generated samples/s, 20 ms worker
delivery, enabled intraday charts, and 16 ms chart sampling.
The module worker keeps mutable quote state private. A deterministic PRNG and a
16 ms budget loop generate samples; a Map coalesces repeated updates for the
same row. A separate publication timer sends the latest unique row updates at
the configured interval. The main thread creates a new outer array and new
objects only for changed rows, preserving every untouched row reference.
History arrays are replaced only when their sampling interval elapses. Session
IDs reject stale messages after reset or row-count changes.
- Synthetic quote workload is worker-side generated samples per second. It
is not pointer events, renders, or messages. - Worker delivery interval controls coalesced
postMessagecadence; 20 ms
targets about 50 messages/s. - Changed rows counts row snapshots applied on the main thread. Rows are
deduplicated within one snapshot, but the same row can be counted again in a
later snapshot, so it is throughput rather than a global distinct-row count. - Message samples shows how much generated work the latest message contains.
The 25K burst deliberately creates and flushes one expensive batch. The worker
mimics an external streaming producer, but network transport is not measured.
Table and interaction architecture
The 14 leaf columns are grouped into Instrument, Price & Change, Order Book,
Session, and Chart. The example supports core sorting/filtering, on-change
resizing, double-click size reset, drag column ordering, row selection, drag
cell ranges, keyboard navigation, and custom Price/Move/Percent/Sparkline cells.
Stable instrument IDs are supplied through getRowId.
table.Subscribe boundaries isolate table state, resize handles, and row
selection. TradingGridPointerController is allocated once for the body and
receives delegated mouse/pointer events. It resolves cells fromevent.composedPath() and data attributes, eliminating listeners per cell.
Row hover is CSS-only.
Column widths are table-level CSS custom properties updated only when sizing or
ordering changes. Initial sizes expand to the viewport through aResizeObserver; manual resizing turns off further auto-fit. The optional move
component A/B mode intentionally destroys/recreates component types and should
not be treated as the normal baseline.
Virtualization
The preference is auto, tanstack, or none:
- below 200 rows,
automeans Full DOM, while Virtual remains selectable; - 200–1,499 rows default to TanStack Virtual but can be switched to Full DOM;
- 1,500 or more rows force Virtual and lock the control.
Because this adapter consumes @tanstack/virtual-core directly, the localuseVirtualizer hook owns the Virtualizer instance and synchronizes its change
notifications with Preact. It uses 32 px rows, 10-row overscan, row IDs as item
keys, transformed rows, and a spacer body. The footer reads the virtualizer
range. Both modes use content-visibility: auto; that browser hint does not
avoid mounting every component in Full DOM mode.
Performance decisions
- worker-side generation and pre-message coalescing;
- structural sharing for unchanged rows and histories;
- stable table/virtualizer keys;
- direct Preact Store atom subscriptions, including an isolated quote atom;
- table/row/resize subscription boundaries;
- one delegated grid interaction controller;
- CSS variables for column sizing;
- opt-in component churn and independently sampled sparklines;
- virtual mounting for large row counts;
- benchmark publication slower than the data stream.
The immutable outer array must change when a batch is applied. Stable inner
references reduce renderer work, but sorting/filtering may still require a core
row-model pass when data changes.
Diagnostics and interpretation
The compact Live health block lives in the configurator sidebar and shows
four cross-framework signals:
- Frame rate (est.) counts
requestAnimationFramecallbacks over a rolling
one-second window. It is capped by display refresh and is not a measurement of
GPU-presented frames. - Average commit latency is a rolling three-second average from the first
pending market mutation to the table's DOM commit. It includes scheduling,
Preact work, and the commit; it is not component render duration. - Long frames comes from the Long Animation Frames API and is cumulative
since reset, with the worst observed duration when supported. - Throughput pairs changed rows/s with applied snapshots/s; changed rows are
deduplicated per snapshot, not across the complete reporting window.
Detailed diagnostics retain worker samples, messages, state applies, table DOM
commits, a 10-second commit-latency p95/max, cumulative slow commits, mounted
hosts, component creation/destruction, cell callbacks by column, component
executions by type, DOM MutationRecord rate, row-model timing, and heap data
where supported.
The in-memory latency and row-model aggregates inspect every call, while the
Performance timeline writes only one User Timing measure per 20 calls to keep
instrumentation from dominating a hot run. The MutationObserver watches the
table body for text/children and only class/style attribute changes. Its
rate counts browser records rather than DOM operations: records can be
coalesced, equivalent UIs can produce different patterns, and creating records
has real overhead. Heap is Chrome-only usedJSHeapSize, is sensitive to
garbage-collection timing, and should be read as a trend rather than proof of a
leak.
The monitor itself adds one lightweight requestAnimationFrame callback and
publishes the sidebar snapshot every 500 ms. That subscription is independent
from the quote atom and table data boundary, but it remains instrumentation
overhead and must stay enabled on both sides of an A/B comparison.
Callback execution does not imply a DOM mutation, and temporary heap growth is
not by itself a leak. Compare identical production configurations and confirm
retention with post-GC heap snapshots. Use browser Performance tooling and
Preact DevTools for call stacks alongside the in-app counters.
Standalone policy
All feed, worker, instrument, shell, style, and benchmark files are copied into
this directory intentionally. The example can run alone or be moved to
StackBlitz without a shared example package, so common implementation and README
sections are duplicated across adapters by design.
The workspace resolves the pinned @tanstack/preact-table dependency to the
repository package while keeping the example manifest release-like.
Overview (`examples/react/realtime-trading/README.md`)
React realtime trading benchmark
This is a standalone stress example for the current TanStack React Table
adapter. It combines a high-frequency synthetic market feed, immutable row
snapshots, sortable and resizable columns, range selection, dynamic React cell
components, optional row virtualization, React Profiler measurements, and
browser performance instrumentation.
The goal is to make feed work, message delivery, state application, table work,
React commits, and browser layout independently observable. It is a repeatable
rendering benchmark, not an exchange or network simulator.
Run and verify
pnpm --dir examples/react/realtime-trading devOpen http://localhost:7777.
For React commit timings, use the profiling production build:
pnpm --dir examples/react/realtime-trading build:profileThe normal verification commands are:
pnpm --dir examples/react/realtime-trading test:types
pnpm --dir examples/react/realtime-trading lint
pnpm --dir examples/react/realtime-trading build
pnpm --dir examples/react/realtime-trading test:e2eDevelopment builds include React checks and produce deliberately pessimistic
timings. The standard production build disables Profiler callbacks; theprofile mode aliases the profiling React DOM client so commit metrics remain
available.
Directory structure
| Path | Responsibility |
|---|---|
src/feed/ |
Market types, instruments, feed configuration, immutable update helpers, controller, and React lifecycle hook. |
src/feed/worker/ |
Worker protocol, deterministic market engine, and the module Web Worker. |
src/benchmark/ |
Browser monitor, benchmark controller, table observers, and React Profiler integration. |
src/shell/ |
Controller contexts, full-viewport shell, metrics, configurator, diagnostics, selected instrument, and status bar. |
src/table/table-config/ |
Grouped columns, row boundary, custom cells, and render counters. |
src/table/ |
Table instance, subscription boundaries, interactions, pointer hook, initial fit, and virtualization. |
src/App.tsx |
Composition root; creates controllers, providers, shell, table, and Profiler boundary. |
Feed state and benchmark state are intentionally separate. MarketFeedController
can run without TradingBenchmarkController; the latter observes feed lifecycle
events and owns only controls/metrics used by this benchmark UI.
Data and worker pipeline
The initial configuration is 100 instruments, 10K generated samples per
second, a 20 ms delivery interval, enabled intraday charts, and 16 ms intraday
sampling.
MarketFeedControllerstarts a module worker and sends the active config.market-feed-engine.tsmutates worker-private quote state using a
deterministic PRNG and stable instrument IDs.- A 16 ms loop accrues a fractional sample budget and produces the requested
amount of synthetic market work. - A
Mapkeyed by row index retains the latest pending update for each
instrument, coalescing repeated samples. - A separate timer publishes at the selected delivery interval. Worker work
rate andpostMessagerate are deliberately independent. - The main thread creates a new outer quotes array and replaces only the rows
present in the message. Unchanged row objects remain referentially stable. - History arrays change only on an intraday sample. A session ID discards stale
messages after reset or instrument-count changes.
UI terminology:
- Synthetic quote workload means generated samples per second inside the
worker, not browser events, React renders, or messages. - Worker delivery interval is the target coalesced-message cadence; 20 ms
targets approximately 50 messages per second. - Changed rows counts immutable row objects applied on the main thread.
Rows are deduplicated within one snapshot, but the same row can be counted
again in a later snapshot, so this is intentionally a throughput rate rather
than a count of distinct instruments over the whole second. - Message samples is the generated work represented by the latest message.
Intraday history sampling is independent of the quote workload, and the 25K
burst creates one intentionally expensive immediate batch. The worker resembles
an upstream WebSocket/SSE producer but does not include network latency.
React state and rendering architecture
MarketFeedControllerexposes each feed value as a direct TanStack atom:quotes, status, row count, workload, delivery, and chart controls do not
share one aggregateMarketFeedStatenotification.quotesis its own high-frequency atom. The table subscribes to it directly,
while the header/configurator subscribe only to their lower-frequency atoms.- Benchmark summary state remains a TanStack Store because its metrics are
published together as one coherent snapshot. createStoreContextprovides the controllers without making every consumer
subscribe to the complete state object.- Shell components call
useSelectorwith narrow slices and shallow comparison
where a selector returns an object. - Selected symbol and renderer mode are dedicated atoms because they have
different consumers and update frequencies from aggregate diagnostics. TradingTablesubscribes directly to quote data and the virtualization
inputs it needs; the parent shell does not receive the quotes array.table.Subscribeboundaries isolate sorting/filtering/order changes, each
resize handle, and each row's selection state.- Stable row IDs come from instrument IDs through
getRowId.
React Compiler is enabled in the Vite Babel pipeline for src. The current
adapter is compiled normally; there is no historical v8 implementation oruse no memo compatibility boundary in this example. Compiler optimization
does not replace correct subscription ownership: components still subscribe to
the smallest practical state slice.
Table behavior
The grid has 14 leaf columns grouped into Instrument, Price & Change, Order
Book, Session, and Chart sections. It provides:
- core sorting and filtering;
- on-change column resizing with double-click reset;
- drag-and-drop leaf-column ordering;
- CSS-only row hover;
- click/modified-click row selection;
- mouse-drag cell range selection and keyboard navigation;
- custom Price, Move, Percent Change, and Sparkline components;
- an opt-in mode that swaps move component A/B as direction changes.
useTradingGridPointer installs handlers once on tbody. It usesevent.composedPath() and data attributes to resolve the TanStack cell, so the
table does not allocate pointer handlers for every cell. Refs retain transient
drag state without triggering React renders.
Column sizes are written to CSS custom properties only when sizing or ordering
changes. Cells reference those properties, avoiding width-object churn during
quote updates. A ResizeObserver expands the initial column sizes to the
available viewport; the first manual resize disables later automatic fitting.
Full DOM and virtual rows
The internal preference is auto, tanstack, or none:
- Below 200 rows,
autoresolves to Full DOM, while virtualization remains
manually selectable. - From 200 through 1,499 rows,
autoresolves to TanStack Virtual, but the
user may still choose Full DOM. - At 1,500 rows or more, virtualization is forced and the control is locked.
React Virtual uses a fixed 32 px estimate, 10-row overscan, stable row IDs as
item keys, transformed rows, and a body-sized spacer. The footer reports the
current range from the virtualizer. Full DOM maps the complete row model.
Both paths apply content-visibility: auto with a matching intrinsic height.
For Full DOM this can reduce browser work but not React element creation or DOM
mount count. Virtualization is what limits mounted row components.
Performance decisions
- Market calculations run outside the main thread.
- Repeated samples are coalesced before worker messaging.
- Immutable snapshots preserve untouched row/history references.
- Stable row keys and
getRowIdpreserve identity through sorting. - Controller contexts carry stable objects; selectors subscribe to slices.
- Table, header, resize handle, row selection, and cell renderer boundaries are
independently subscribable. - Pointer selection is delegated and transient pointer state lives in refs.
- Column widths use CSS variables instead of per-tick style recalculation.
- Dynamic component destruction is opt-in; stable components are the baseline.
- Virtualization limits React and DOM work at larger row counts.
- Benchmark publication is throttled separately from the feed.
A new outer data array is required to publish immutable state. Referentially
stable untouched rows, keyed rows, and narrow subscriptions reduce downstream
work; they do not guarantee that the table's sorted/filtered row model can skip
all processing when its data input changes.
Diagnostics
The compact Live health block lives in the configurator sidebar and keeps
four cross-framework signals visible:
- Frame rate (est.) counts
requestAnimationFramecallbacks over a rolling
one-second window. It is a main-thread scheduling signal capped by the
display refresh rate, not proof of GPU-presented frames or a React Scan FPS
value. - Average commit latency is the rolling three-second average from the first
pending market mutation to the table's DOM commit. Several snapshots may be
coalesced behind one commit, so this includes scheduling, React work, and DOM
commit latency; it is not component render duration. - Long frames comes from the browser Long Animation Frames API and is
cumulative since reset. The worst duration is shown when supported. - Throughput pairs changed rows/s with applied snapshots/s. Changed rows are
deduplicated per snapshot, not across the full reporting window.
The detailed section retains generated worker samples, messages, state applies,
table DOM commits, a 10-second commit-latency p95/max, cumulative slow commits,
mounted hosts, component lifecycle counts, callbacks by column, executions by
component type, DOM MutationRecord rate, core row-model timing, and optional
heap information.
The React Profiler additionally records actual/base duration and commit counts
when using development or the profiling build. The in-memory latency, Profiler,
and row-model aggregates inspect every call, but the Performance timeline emits
only one User Timing measure per 20 calls to avoid making instrumentation a
significant part of a hot run. Old measures are periodically pruned.
The MutationObserver watches the table body for text/children and onlyclass/style attribute changes. Its value is browser MutationRecords per
second, not DOM operations: a record can represent a coalesced change, and
framework write patterns can produce different record counts for equivalent
screens. Creating records also has real overhead at high rates, so treat it as
a diagnostic and confirm important comparisons with a Performance recording.
Heap is Chrome-only usedJSHeapSize, changes with garbage-collection timing,
and is useful as a trend rather than a leak verdict.
The monitor itself schedules one lightweight requestAnimationFrame callback
and publishes its sidebar snapshot every 500 ms. The sidebar subscribes to that
snapshot independently, so publishing diagnostics does not update the quotes
atom or table data boundary, but it is still instrumentation overhead and must
remain enabled in both sides of an A/B comparison. React Profiler and React
DevTools also add overhead by design.
Callback counts are not DOM mutation counts. A cell callback can execute while
React reuses the existing component and DOM. Likewise, a rising heap during the
component-swap stress test is not proof of a leak until repeated post-GC heap
snapshots show retained instances.
Use Chrome Performance and React DevTools for call stacks/flamegraphs, and keep
the instrument count, workload, delivery interval, renderer mode, build mode,
and virtualization mode fixed between comparisons.
React Scan remains an optional development-only aid in this example. It is not
used by any canonical counter because the other adapter examples cannot share
it, and its instrumentation can alter a recording. Use the profiling production
build without React Scan for cross-framework comparisons.
Standalone example policy
This directory owns its instrument list, feed engine, worker, diagnostics,
styles, and UI instead of importing a common demo package. The duplication is
intentional: each adapter example must run independently and remain easy to
copy to StackBlitz. Shared architectural explanations are repeated in the
READMEs for the same reason.
The package pins @tanstack/react-table to the repository version. The root
workspace override resolves it to packages/react-table while preserving a
release-like manifest.
Overview (`examples/solid/realtime-trading/README.md`)
Solid realtime trading benchmark
This standalone example exercises the current TanStack Solid Table adapter
with a high-frequency worker feed, immutable snapshots, fine-grained reactive
cells, table interactions, virtual rows, and browser diagnostics. It is a
repeatable UI workload, not an exchange or network benchmark.
Run and verify
pnpm --dir examples/solid/realtime-trading devOpen http://localhost:7777.
pnpm --dir examples/solid/realtime-trading test:types
pnpm --dir examples/solid/realtime-trading lint
pnpm --dir examples/solid/realtime-trading build
pnpm --dir examples/solid/realtime-trading test:e2eRecord performance against the production build; development checks distort
absolute timings.
Structure and ownership
| Path | Responsibility |
|---|---|
src/feed/ |
Market model, instrument universe, feed config, immutable update logic, and Solid feed controller. |
src/feed/worker/ |
Protocol, deterministic market engine, and module worker. |
src/benchmark/ |
Benchmark monitor and the controller that exposes benchmark signals/actions. |
src/shell/ |
Contexts, viewport shell, header, metrics, configurator, diagnostics, and selected instrument. |
src/table/table-config/ |
Declarative columns, named cell renderers, component props, and quote value calculations. |
src/table/ |
Table model primitive, view components, interactions, layout, and virtualization. |
src/App.tsx |
Creates feed/benchmark controllers and composes their providers with the table. |
createMarketFeedController and createTradingBenchmarkController are
separate. The feed owns worker state and quotes; the benchmark observes feed
events and owns only diagnostic/view state. Solid contexts provide controller
objects, and consumers read individual accessors rather than one global state
snapshot.
Feed and worker pipeline
The initial setup is 100 instruments, 10K generated samples/s, 20 ms delivery,
enabled intraday charts, and 16 ms chart sampling.
Mutable quotes never leave the worker. A deterministic generator and a 16 ms
budget loop create samples, while a row-indexed Map coalesces repeated changes.
A separate timer publishes the latest unique updates. On the main thread the
outer quote array is new, changed rows are new, and untouched rows keep their
references. History arrays change only at their own sampling cadence. Session
IDs prevent stale batches from an old configuration from being applied.
- Synthetic quote workload is generated worker samples/s, not messages or
framework updates. - Worker delivery interval controls coalesced messages; 20 ms targets about
50 messages/s. - Row updates is the unique immutable rows applied by a message.
- Message samples is the worker work represented by the latest message.
The 25K burst immediately generates and flushes an intentionally heavy batch.
The worker is a local stand-in for an upstream stream; network latency is not
part of the test.
Solid table architecture
The table has 14 leaf columns grouped into Instrument, Price & Change, Order
Book, Session, and Chart. It supports sorting/filtering, on-change resizing,
double-click reset, drag ordering, row selection, drag cell ranges, keyboard
navigation, and component-based Price/Move/Percent/Sparkline cells.
The table reads the feed's quote accessor directly from context; App does not
subscribe to or forward quote snapshots. TradingTable is only the composition
root for createTradingTableModel, row virtualization, layout and commit
effects, plus TradingTableHeader, TradingTableBody, andTradingTableFooter. Stable instrument IDs back getRowId. Full-DOM rows use
Solid's position-owned `` scopes and a dedicated TradingTableRow
boundary. Each position receives the current TanStack Row. One row-level memo
keeps its render cells while the row ID, column configuration, and cell order
are unchanged. A small Solid context exposes an Accessor<MarketQuote> for that
row, so named renderers and their component props update without replacing the
mounted FlexRender. It is a normal accessor, not a proxy or mutable registry.
Row and cell DOM attributes are written directly in JSX. Every cell slot also
receives the current row-model cell at the same visible-column index; selection
edges, focus, selected state, and roving tabindex are computed from that current
cell. This avoids stale selection geometry without getAllCellsByColumnId() or
per-cell row/column ID memos. Grid-level pointer interaction remains delegated
to the table body.
<For> is intentionally not used for the immutable TanStack row model. Solid
keys <For> by item identity, while TanStack creates new Row objects after a
new data array. Stable getRowId values preserve table state, but do not change
Solid's identity comparison; <For> would therefore treat every new row object
as a replacement. `` keeps the DOM/component slot mounted while replacing
the current Row and selection Cell props at that position. The separate
render-cell memo changes only for a new row ID, column definition/order, or row
moving into the slot, so quote snapshots update component props without
remounting FlexRender. Sorting updates the affected positions as expected.
The column configuration is memoized by renderer mode. Changing Stable to A/B
therefore refreshes the column definition once, while normal quote batches do
not rebuild columns. trading-columns.tsx contains only the declarative schema;
named renderers, component prop interfaces, and market calculations live in
focused colocated modules. In A/B mode the Move renderer selects the direction
component when that row changes, intentionally testing mount/unmount churn.
createTradingGridSelectionHandlers creates one delegated body handler object
and resolves cells from composedPath() plus identity data attributes. No
pointer listener is added per cell, and hover remains CSS-only. Header drag,
grid attributes, and body attributes use focused reusable prop factories; the
hot row/cell path stays as direct JSX. Column dimensions live in CSS variables
and update only for column sizing/order; a ResizeObserver performs the initial
fit until the user resizes manually.
Virtualization
- Below 200 rows, automatic mode chooses Full DOM, but Virtual is selectable.
- From 200 through 1,499 rows, automatic mode chooses TanStack Virtual and the
user may still choose Full DOM. - At 1,500 rows or more, Virtual is forced and the control is disabled.
createVirtualizer uses 32 px row estimates, 10-row overscan, stable row IDs,
transformed rows, and a spacer body. Its range drives the current-row footer.
Both rendering paths use content-visibility: auto; in Full DOM it can skip
some browser rendering but does not avoid creating every Solid row/cell.
Performance decisions
- market work and coalescing happen before crossing to the main thread;
- immutable snapshots preserve unchanged row/history references;
- fine-grained signals avoid broad shell or root invalidation;
- position-owned rows/cells preserve DOM slots and render-cell identity while
an accessor updates the current immutable quote; - delegated grid interaction avoids per-cell handlers;
- CSS variables isolate width changes from quote updates;
- dynamic component churn and sparkline frequency are explicit controls;
- virtualization limits mounted DOM for larger data;
- metrics publish at a lower cadence than feed updates.
A new outer array is part of the immutable contract. Structural sharing helps
cell rendering, but table sorting/filtering can still rebuild a row model when
the data accessor changes.
Diagnostics and interpretation
The sidebar starts with four cross-framework health signals: estimated rAF
callbacks/s over one second, average snapshot-to-DOM-commit latency over three
seconds, long animation frames accumulated since reset, and throughput as
changed rows/s plus applied snapshots/s. “Changed rows” is deduplicated within
each snapshot; the same instrument can count again in a later snapshot. The
advanced diagnostics retain worker samples/messages, DOM commits, a rolling
10-second p95/max commit latency, slow commits, lifecycle/execution rates, DOM
mutation records, and optional heap information.
The frame figure is deliberately labeled estimated: it counts this page's rAF
callbacks, is capped by the display refresh rate, and falls when a tab is
throttled. It is a portable responsiveness signal, not compositor-presented
FPS.
Solid applies reactive DOM writes synchronously, but the quote-tracking effect
queues one coalesced microtask before closing a pending commit measurement. This
prevents an effect scheduled early in the reactive graph from reporting a
commit before child bindings have settled. User Timing timeline entries are
sampled at one in 20 commits; the in-memory latency calculation still records
every commit.
Renderer callbacks are not equivalent to DOM mutations. With this example's
immutable outer array, TanStack can produce new row/cell instances, but the
row-ID equality boundary keeps FlexRender stable across quote snapshots.
Column mode/order changes, sorting, and virtual-slot reuse replace the relevant
render cell. A/B mode additionally replaces the direction component when the
sign changes. A rising heap during
component swapping is not proof of a leak without stable post-GC retention.
The heap value is Chromium-only, represents the current GC-sensitive JS heap,
and is not a retained-size measurement. The DOM rate counts MutationRecord
objects, not individual browser operations; records may be coalesced. Its
observer watches text/child changes and only class/style attributes to
reduce, but not eliminate, observer overhead. Non-feed text/child changes and
interaction-driven class/style changes (including virtual scrolling) are
included, so do not interpret the rate as feed-only work. Use a production
build, identical controls, Chrome Performance, and Solid DevTools for meaningful
comparisons.
Standalone policy
This directory intentionally owns copies of the feed, worker, instruments,
benchmark code, UI, and styles. That makes it independent and StackBlitz-ready;
shared code and explanatory README sections are duplicated across adapters by
design.
The workspace resolves the pinned @tanstack/solid-table dependency to the
local adapter package while keeping the manifest release-like.
Overview (`examples/svelte/realtime-trading/README.md`)
Svelte realtime trading benchmark
This standalone example exercises the current TanStack Svelte Table adapter
with a high-frequency worker feed, immutable market snapshots, interactive
columns, custom Svelte cells, virtual rows, and browser diagnostics. It is a
repeatable rendering workload rather than an exchange/network simulator.
Run and verify
pnpm --dir examples/svelte/realtime-trading devOpen http://localhost:7777.
pnpm --dir examples/svelte/realtime-trading test:types
pnpm --dir examples/svelte/realtime-trading lint
pnpm --dir examples/svelte/realtime-trading build
pnpm --dir examples/svelte/realtime-trading test:e2eUse a production build for performance recordings.
Structure and ownership
| Path | Responsibility |
|---|---|
src/feed/ |
Market model, instrument universe, feed config, immutable updates, and feed controller. |
src/feed/worker/ |
Typed worker protocol, deterministic engine, and module worker. |
src/benchmark/ |
Browser monitor, benchmark controller, table observer, and row-model timing. |
src/shell/ |
Svelte context plus separate header, metrics, configurator, diagnostics, selected-instrument, and status components. |
src/table/table-config/ |
Grouped columns and dedicated Price/Move/Percent/Sparkline Svelte components. |
src/table/ |
Svelte Table setup, table view, delegated interactions, column layout, and Svelte Virtual integration. |
src/App.svelte |
Creates/provides controllers and starts/stops them with the app lifecycle. |
The feed and benchmark are separate controllers. The feed owns worker state and
quotes; the benchmark registers observers and owns metrics/view controls.
Svelte context passes those controllers without routing the quote array through
the root component. quotes is a dedicated high-frequency TanStack atom, and
each feed status/configuration value is a separate atom. Shell pieces subscribe
only to the atoms they display; benchmark metrics remain a coherent snapshot
store.
Feed and worker pipeline
The default is 100 instruments, 10K generated samples/s, 20 ms delivery,
enabled intraday charts, and 16 ms chart sampling.
Worker-private mutable quotes are changed by a deterministic 16 ms budget loop.
A row-indexed Map coalesces repeated instrument changes. An independent timer
publishes the latest unique rows at the selected cadence. Applying a message
creates a new outer array and new objects only for changed rows; untouched rows
and unsampled histories retain their references. Session IDs prevent late
messages from an old reset/row-count session from entering current state.
- Synthetic quote workload is worker-side generated samples/s, not messages
or Svelte updates. - Worker delivery interval controls coalesced messages; 20 ms targets around
50 messages/s. - Row updates counts unique immutable row objects applied.
- Message samples is the generated work represented by the latest message.
Intraday history sampling is independent. The 25K burst deliberately publishes
one heavy batch. This resembles an upstream stream but excludes network delay.
Svelte table architecture
The 14 leaf columns are grouped into Instrument, Price & Change, Order Book,
Session, and Chart. Sorting/filtering, on-change resizing, double-click reset,
drag column ordering, row selection, drag cell ranges, keyboard navigation,
CSS-only hover, and component-based quote cells are included.
TradingTable.svelte owns the view, while trading-table.ts andtable-config/ isolate table construction and columns. Stable instrument IDs
back row identity. The custom quote components make component executions and
lifecycle churn measurable; the A/B move mode intentionally changes component
type and is not the normal rendering baseline.
Selection uses one delegated body-level interaction controller and resolves
cells from composedPath() plus data attributes. Column dimensions are CSS
custom properties updated only for sizing/order. A ResizeObserver performs
initial fitting and yields after the user resizes.
Virtualization
- Below 200 rows, automatic mode chooses Full DOM, but Virtual can be enabled.
- From 200 through 1,499 rows, automatic mode chooses TanStack Virtual and Full
DOM remains selectable. - At 1,500 rows or more, Virtual is forced and the control is disabled.
Svelte Virtual uses a 32 px estimate, 10-row overscan, row IDs for item keys,
transformed rows, and a spacer body. The virtualizer range feeds the current-row
footer. Both modes use content-visibility: auto; Full DOM still creates every
Svelte row/cell even if the browser skips offscreen paint/layout work.
Performance decisions
- worker generation and pre-message coalescing;
- immutable structural sharing for unchanged rows/history;
- controller context instead of root-level data prop drilling;
- stable keyed row identity;
- table/config/cell component boundaries with localized atom/store reads;
- delegated pointer selection and CSS hover;
- CSS variables for column width propagation;
- configurable chart frequency and opt-in component churn;
- virtual mounting for larger row sets;
- low-frequency metric publication relative to feed updates.
Publishing immutable state necessarily changes the outer array. Stable inner
references reduce downstream work, but sorting/filtering can still require a
new table row-model pass.
Diagnostics and interpretation
The sidebar starts with four cross-framework health signals: estimated rAF
callbacks/s over one second, average snapshot-to-DOM-commit latency over three
seconds, long animation frames accumulated since reset, and throughput as
changed rows/s plus applied snapshots/s. “Changed rows” is deduplicated within
each snapshot; the same instrument can count again in a later snapshot. The
advanced diagnostics retain worker samples/messages, DOM commits, a rolling
10-second p95/max commit latency, slow commits, lifecycle/execution rates,
row-model timing, DOM mutation records, and optional heap information.
The frame figure is deliberately labeled estimated: it counts this page's rAF
callbacks, is capped by the display refresh rate, and falls when a tab is
throttled. It is a portable responsiveness signal, not compositor-presented
FPS.
Svelte waits for tick() before closing a pending DOM-commit measurement. User
Timing entries for commits and row-model work are sampled at one in 20 calls;
the in-memory counters and latency windows still measure every call.
Renderer callbacks and DOM mutations measure different layers. Heap growth is
not automatically a leak; verify retained objects after GC. The heap value is
Chromium-only and GC-sensitive, not retained size. The DOM rate countsMutationRecord objects rather than browser operations, and records may be
coalesced; the observer watches text/child changes plus only class/style
attributes to reduce its own overhead. Non-feed text/child changes and
interaction-driven class/style changes (including virtual scrolling) are
included, so this is not a feed-only rate. Use identical production settings,
Chrome Performance, and Svelte DevTools when comparing runs.
Standalone policy
This directory deliberately contains copies of the feed, worker, instruments,
benchmark, shell, styles, and table code. It can run independently or be copied
to StackBlitz, so shared code and README sections are repeated across adapters
by design.
The workspace resolves the pinned @tanstack/svelte-table dependency to the
local adapter package while preserving a release-like manifest.
Overview (`examples/vue/realtime-trading/README.md`)
Vue realtime trading benchmark
This standalone example exercises the current TanStack Vue Table adapter with
a high-frequency worker feed, immutable quote snapshots, interactive columns,
custom Vue cells, optional virtualization, and browser diagnostics. It is a
repeatable UI stress workload, not an exchange or network benchmark.
Run and verify
pnpm --dir examples/vue/realtime-trading devOpen http://localhost:7777.
pnpm --dir examples/vue/realtime-trading test:types
pnpm --dir examples/vue/realtime-trading lint
pnpm --dir examples/vue/realtime-trading build
pnpm --dir examples/vue/realtime-trading test:e2eUse the production build for recordings; Vue development checks add overhead.
Structure and ownership
| Path | Responsibility |
|---|---|
src/feed/ |
Market model, instrument universe, feed configuration, immutable updates, and feed controller. |
src/feed/worker/ |
Typed protocol, deterministic market engine, and module worker. |
src/benchmark/ |
Browser monitor, benchmark controller, table timing, lifecycle, and mutation observers. |
src/shell/ |
Controller injection, viewport shell, controls, metrics, selected instrument, and diagnostics. |
src/table/table-config/ |
Grouped columns and custom Vue quote cells. |
src/table/ |
Vue Table instance, delegated interactions, column layout, and Vue Virtual integration. |
src/App.tsx |
Creates/starts controllers, provides them, and composes shell plus table. |
MarketFeedController owns worker/feed state; TradingBenchmarkController
observes it and owns benchmark/view state. Vue provide/inject distributes
stable controller objects. The controllers use Vue primitives directly:quotes and metrics are independent shallowRef values, while feed status,
configuration, selection, and view state use focused ref values. Consumers
read only the refs they need, and the root never consumes the quote stream.
Cleanup stops both controllers on application unmount.
Feed and worker pipeline
The default run uses 100 instruments, 10K generated samples/s, 20 ms delivery,
enabled intraday charts, and 16 ms chart sampling.
The worker keeps mutable market state private. Its deterministic generator runs
from a 16 ms accrued budget, while a row-indexed Map coalesces repeated
updates. A separate publication timer sends the latest unique rows. The main
thread publishes a new quote array but replaces only changed row objects;
untouched rows and unsampled history arrays keep their references. Session IDs
discard messages from an obsolete reset/instrument configuration.
- Synthetic quote workload is generated samples/s in the worker, not Vue
renders orpostMessagecalls. - Worker delivery interval is the target coalesced-message cadence; 20 ms
targets roughly 50 messages/s. - Row updates counts unique immutable rows applied by a message.
- Message samples is the generated work represented by the latest batch.
Chart sampling is independent of price sampling. The 25K burst immediately
creates and publishes one heavy batch. Worker messaging approximates an
upstream stream, but actual network latency is outside the benchmark.
Vue table architecture
The grid has 14 leaf columns grouped into Instrument, Price & Change, Order
Book, Session, and Chart. It supports sorting/filtering, on-change resizing,
double-click reset, drag ordering, CSS row hover, row selection, drag cell
ranges, keyboard navigation, and component-based Price/Move/Percent/Sparkline
cells.
The table accepts the dedicated quotes shallowRef directly and uses the
instrument ID for getRowId. TanStack Vue Table's state atoms are Vue-backed,
so row-model computations and layout watchers read only the required atoms from
native computed/watch boundaries. Dedicated row views keep row markup and
selected-instrument state below the whole-table boundary.
A single TradingGridPointerController handles body pointer events. It mapsevent.composedPath() and data attributes back to the TanStack cell, avoiding
one handler per cell. Column sizes are CSS variables updated only when sizing
or order changes. ResizeObserver performs initial fit until manual resizing.
The A/B Move renderer is an explicit component lifecycle stress mode.
Virtualization
- Below 200 rows, automatic mode uses Full DOM, but Virtual is selectable.
- From 200 through 1,499 rows, automatic mode uses TanStack Virtual, while Full
DOM remains selectable. - At 1,500 rows or more, Virtual is forced and its control is locked.
Vue Virtual uses 32 px row estimates, 10-row overscan, instrument IDs as item
keys, transformed rows, and a spacer body. The visible-range footer reads the
virtualizer range. Both paths apply content-visibility: auto; in Full DOM it
can reduce browser rendering but cannot prevent Vue from creating all rows.
Performance decisions
- market generation/coalescing is moved off the main thread;
- immutable structural sharing preserves untouched rows/histories;
- focused Vue refs and computed values narrow reactive invalidation;
- stable row and virtual item keys preserve identity;
- row rendering is componentized without subscribing the app root to data;
- pointer selection is delegated once;
- CSS variables separate width changes from quote updates;
- component swapping and chart frequency are opt-in stress controls;
- virtualization limits framework and DOM mounts;
- metrics are published more slowly than the feed.
The immutable outer array must change for a batch. Structural sharing can reduce
cell work, but sorted/filtered row models may still run when the data input
changes.
Diagnostics and interpretation
The sidebar starts with four cross-framework health signals: estimated rAF
callbacks/s over one second, average snapshot-to-DOM-commit latency over three
seconds, long animation frames accumulated since reset, and throughput as
changed rows/s plus applied snapshots/s. “Changed rows” is deduplicated within
each snapshot; the same instrument can count again in a later snapshot. The
advanced diagnostics retain worker samples/messages, DOM commits, a rolling
10-second p95/max commit latency, slow commits, lifecycle/execution rates,
row-model timing, DOM mutation records, and optional heap data.
The frame figure is deliberately labeled estimated: it counts this page's rAF
callbacks, is capped by the display refresh rate, and falls when a tab is
throttled. It is a portable responsiveness signal, not compositor-presented
FPS.
Vue closes the pending measurement from the table's mounted/updated commit
hooks. User Timing entries for commits and row-model work are sampled at one in
20 calls; the in-memory counters and latency windows still measure every call.
Callback counts are not DOM mutation counts. Temporary heap growth during the
swap workload is not a leak unless post-GC snapshots retain instances. The heap
value is Chromium-only and GC-sensitive, not retained size. The DOM rate countsMutationRecord objects rather than browser operations, and records may be
coalesced; the observer watches text/child changes plus only class/style
attributes to limit its own overhead. Non-feed text/child changes and
interaction-driven class/style changes (including virtual scrolling) are
included, so this is not a feed-only rate. Compare identical production
configurations and use Chrome Performance plus Vue DevTools for call stacks and
component detail.
Standalone policy
The instruments, feed, worker, benchmark, shell, styles, and table code are
intentionally copied into this folder. This keeps the example independently
runnable and StackBlitz-ready, so shared implementation and README explanations
are duplicated across adapters by design.
The workspace resolves the pinned @tanstack/vue-table dependency to the local
adapter package while retaining a release-like manifest.
Overview (`docs/overview.md`)
title: Overview
[!NOTE]
If you are upgrading from TanStack Table v8, start with the migration guide for your framework:
React
Preact
Octane
- Octane support is new in v9, so there is nothing to migrate. Start with the Octane Quick Start.
Solid
Svelte
Vue
Angular
Ember
- Ember support is new in v9, so there is nothing to migrate. Start with the Quick Start.
Lit
Alpine
- Alpine support is new in v9, so there is nothing to migrate. Start with the Quick Start.
Vanilla
- The vanilla
@tanstack/table-coreentry point changed in v9. See the Vanilla Quick Start page for the new setup.
TanStack Table is the best way to build your own powerful table and datagrid components with blazingly fast performance, but most importantly, with 100% control over your own design and markup.
In this new age of AI, where markup can be generated in seconds, the old value proposition of pre-built components (saving you the typing) matters less than ever. What matters now is owning and controlling the result, and that is exactly what TanStack Table gives you.
TanStack Table might be a bit of a different table library than you are used to. It is NOT a pre-built table component, like you might find from a library like AG Grid. Instead, TanStack Table is a headless UI library that gives you the power to build your own fully customizable table and datagrid components the right way with whatever JavaScript libraries, component libraries, or design systems you want. TanStack Table is the engine that you can hook up to your own favorite front-end tech, no matter what you're already using or want to use.
At its core, TanStack Table is agnostic. Most table data-processing and state logic is written in framework-agnostic TypeScript, and framework adapters connect that core to your favorite frameworks. Official adapters are provided for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit, and Alpine, or you can use the core directly in vanilla JavaScript via @tanstack/table-core.
It also does not care which CSS or component library you use, and is compatible with anything from Tailwind, Bootstrap, Material UI, ShadCN UI, or even your own custom design system.
What is "Headless" UI?
Headless UI is a term for libraries and utilities that provide the logic, state, processing, and APIs for UI elements and interactions, but do not provide markup, styles, or pre-built implementations. The hardest parts of building complex UIs usually revolve around state, events, side effects, and data processing. By removing those concerns from the markup and styles, your logic becomes more modular and reusable, while you keep complete control over the look and feel. When you use a headless library like TanStack Table, the complex work of data processing, state management, and business logic is handled for you, so you can focus on the higher-cardinality decisions that differ across designs and use cases.
Want to dive deeper? Read more about Headless UI.
Here's the difference in practice. With a pre-built table component, you pass props in and style the result from the outside, limited to whatever the component's props and theme APIs allow. With TanStack Table, you create a table instance and use its state and APIs to render your own markup:
React
- {/* Pass props into a pre-built component and hope you can style it */}
- <PrebuiltDataGrid
- data={data}
- columns={columns}
- theme={messyThemeOverrides}
- sx={cssOverridesThatHopefullyWork}
- positionPagination={"however-my-stakeholders-are-feeling-today"}
- />
+ // Build your own markup from the table instance's state and APIs
+ const table = useTable({ features, columns, data })
+
+ return (
+ <table className="anything-you-want">
+ <thead>
+ {/* need to really customize the functionality in your headers? no problem! */}
+ {table.getHeaderGroups().map((headerGroup) => ( ... ))}
+ </thead>
+ <tbody>
+ {/* want to add virtualization here? no problem! */}
+ {table.getRowModel().rows.map((row) => ( ... ))}
+ </tbody>
+ </table>
+ )Preact
- {/* Pass props into a pre-built component and hope you can style it */}
- <PrebuiltDataGrid
- data={data}
- columns={columns}
- theme={messyThemeOverrides}
- sx={cssOverridesThatHopefullyWork}
- positionPagination={"however-my-stakeholders-are-feeling-today"}
- />
+ // Build your own markup from the table instance's state and APIs
+ const table = useTable({ features, columns, data })
+
+ return (
+ <table className="anything-you-want">
+ <thead>
+ {/* need to really customize the functionality in your headers? no problem! */}
+ {table.getHeaderGroups().map((headerGroup) => ( ... ))}
+ </thead>
+ <tbody>
+ {/* want to add virtualization here? no problem! */}
+ {table.getRowModel().rows.map((row) => ( ... ))}
+ </tbody>
+ </table>
+ )Octane
- {/* Pass props into a pre-built component and hope you can style it */}
- <PrebuiltDataGrid
- data={data}
- columns={columns}
- theme={messyThemeOverrides}
- sx={cssOverridesThatHopefullyWork}
- positionPagination={"however-my-stakeholders-are-feeling-today"}
- />
+ // Build your own markup from the table instance's state and APIs
+ const table = useTable({ features, columns, data })
+
+ <table className="anything-you-want">
+ <thead>
+ {/* need to really customize the functionality in your headers? no problem! */}
+ @for (const headerGroup of table.getHeaderGroups(); key headerGroup.id) { ... }
+ </thead>
+ <tbody>
+ @for (const row of table.getRowModel().rows; key row.id) { ... }
+ </tbody>
+ </table>Solid
- {/* Pass props into a pre-built component and hope you can style it */}
- <PrebuiltDataGrid
- data={data()}
- columns={columns}
- theme={messyThemeOverrides}
- sx={cssOverridesThatHopefullyWork}
- positionPagination={"however-my-stakeholders-are-feeling-today"}
- />
+ // Build your own markup from the table instance's state and APIs
+ const table = createTable({ features, columns, get data() { return data() } })
+
+ return (
+ <table class="anything-you-want">
+ <thead>
+ {/* need to really customize the functionality in your headers? no problem! */}
+ <For each={table.getHeaderGroups()}>{(headerGroup) => ( ... )}</For>
+ </thead>
+ <tbody>
+ {/* want to add virtualization here? no problem! */}
+ <For each={table.getRowModel().rows}>{(row) => ( ... )}</For>
+ </tbody>
+ </table>
+ )Svelte
-
- <PrebuiltDataGrid
- {data}
- {columns}
- theme={messyThemeOverrides}
- sx={cssOverridesThatHopefullyWork}
- positionPagination="however-my-stakeholders-are-feeling-today"
- />
+
+ <script>
+ const table = createTable({ features, columns, get data() { return data } })
+ </script>
+
+ <table class="anything-you-want">
+ <thead>
+
+ {#each table.getHeaderGroups() as headerGroup} ... {/each}
+ </thead>
+ <tbody>
+
+ {#each table.getRowModel().rows as row} ... {/each}
+ </tbody>
+ </table>Vue
-
- <PrebuiltDataGrid
- :data="data"
- :columns="columns"
- :theme="messyThemeOverrides"
- :sx="cssOverridesThatHopefullyWork"
- position-pagination="however-my-stakeholders-are-feeling-today"
- />
+
+ <script setup>
+ const table = useTable({ features, columns, data })
+ </script>
+
+ <template>
+ <table class="anything-you-want">
+ <thead>
+
+ <tr v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">...</tr>
+ </thead>
+ <tbody>
+
+ <tr v-for="row in table.getRowModel().rows" :key="row.id">...</tr>
+ </tbody>
+ </table>
+ </template>Angular
-
- <prebuilt-data-grid
- [data]="data"
- [columns]="columns"
- [theme]="messyThemeOverrides"
- [styleOverrides]="cssOverridesThatHopefullyWork"
- positionPagination="however-my-stakeholders-are-feeling-today"
- />
+ // Build your own markup from the table instance's state and APIs
+ readonly table = injectTable(() => ({ features, columns, data: this.data() }))
+
+
+ <table class="anything-you-want">
+ <thead>
+
+ @for (headerGroup of table.getHeaderGroups(); track headerGroup.id) { ... }
+ </thead>
+ <tbody>
+
+ @for (row of table.getRowModel().rows; track row.id) { ... }
+ </tbody>
+ </table>Ember
-
- <PrebuiltDataGrid
- @data={{this.data}}
- @columns={{this.columns}}
- @theme={{this.messyThemeOverrides}}
- @styleOverrides={{this.cssOverridesThatHopefullyWork}}
- @positionPagination="however-my-stakeholders-are-feeling-today"
- />
+ // Build your own markup from the table instance's state and APIs
+ table = useTable(() => ({ features, columns, data: this.data }))
+
+ get rows() { return this.table.getRowModel().rows }
+ get headerGroups() { return this.table.getHeaderGroups() }
+
+ <template>
+ <table class="anything-you-want">
+ <thead>
+ {{! need to really customize the functionality in your headers? no problem! }}
+ {{#each this.headerGroups as |headerGroup|}} ... {{/each}}
+ </thead>
+ <tbody>
+ {{! want to add virtualization here? no problem! }}
+ {{#each this.rows as |row|}} ... {{/each}}
+ </tbody>
+ </table>
+ </template>Lit
-
- <prebuilt-data-grid
- .data=${data}
- .columns=${columns}
- .theme=${messyThemeOverrides}
- .sx=${cssOverridesThatHopefullyWork}
- positionPagination="however-my-stakeholders-are-feeling-today"
- ></prebuilt-data-grid>
+ // Build your own markup from the table instance's state and APIs
+ private tableController = new TableController(this)
+
+ render() {
+ const table = this.tableController.table({ features, columns, data: this.data })
+
+ return html`
+ <table class="anything-you-want">
+ <thead>
+
+ ${table.getHeaderGroups().map((headerGroup) => html`...`)}
+ </thead>
+ <tbody>
+
+ ${table.getRowModel().rows.map((row) => html`...`)}
+ </tbody>
+ </table>
+ `
+ }Alpine
-
- <prebuilt-data-grid
- data="data"
- columns="columns"
- theme="messy-theme-overrides"
- position-pagination="however-my-stakeholders-are-feeling-today"
- ></prebuilt-data-grid>
+ // Build your own markup from the table instance's state and APIs
+ Alpine.data('myTable', () => {
+ const table = createTable({ features, columns, get data() { return data } })
+ return { table }
+ })
+
+
+ <table class="anything-you-want" x-data="myTable">
+ <thead>
+
+ <template x-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">...</template>
+ </thead>
+ <tbody>
+
+ <template x-for="row in table.getRowModel().rows" :key="row.id">...</template>
+ </tbody>
+ </table>Vanilla
- // Configure a pre-built widget and hope you can style it
- new PrebuiltDataGrid('#my-table', {
- data,
- columns,
- theme: messyThemeOverrides,
- positionPagination: 'however-my-stakeholders-are-feeling-today',
- })
+ // Build your own markup from the table instance's state and APIs
+ const table = constructTable({ features, columns, data })
+
+ // render however you like: DOM APIs, innerHTML, or your own template system
+ table.getHeaderGroups().forEach((headerGroup) => { ... })
+ table.getRowModel().rows.forEach((row) => { ... })Features
TanStack Table is packed with features, but you only ever bundle the ones you use. The full v9 package is about 25 KB when minified and Brotli-compressed, but more importantly, it is modular! You register only the features and row models that your table actually uses, and your bundler can tree-shake away unused optional capabilities. A basic table bundles just the small core. Because features vary in size and share that core, the exact savings depend on which capabilities you register.
We like to think of TanStack Table as more of a "system" for building tables than just a library, as it is one of the most customizable table libraries ever built. TanStack Table has the following built-in features, plus a custom feature (plugin) system for adding your own state and APIs.
React
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Preact
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Octane
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Solid
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Svelte
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Vue
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Angular
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Ember
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Lit
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Alpine
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Vanilla
- Cell Selection - Select spreadsheet-style rectangular ranges of cells
- Cell Spanning - Merge adjacent body cells across rows or columns
- Column Filtering - Filter rows based on search values for a column
- Column Grouping - Group rows by one or more column values
- Column Ordering - Dynamically change the order of columns
- Column Pinning - Pin (Freeze) columns to the left or right of the table
- Column Resizing - Let users resize columns with drag handles
- Column Sizing - Dynamically change the size of columns
- Column Visibility - Hide/show columns
- Faceting - List unique values or min/max values for a column or for the entire table
- Global Filtering - Filter rows based on search values for the entire table
- Row Aggregation - Calculate summary values for grouped, root, or custom row sets
- Row Expanding - Expand/collapse rows (sub-rows)
- Row Pagination - Paginate rows
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
- Row Selection - Select/deselect rows (checkboxes)
- Row Sorting - Sort rows by column values
Composition
Headless doesn't just mean "bring your own styles". Because you own the markup, the event handlers, and (optionally) the state, TanStack Table composes cleanly with whatever else your table needs:
- Your own state: Any slice of table state (sorting, filters, pagination, etc.) can be lifted out of the table and managed however you like: in your framework's state, in a global store, or even in the URL.
- Your own features: Custom features let you add your own state and APIs directly to the table instance, right alongside the built-in ones. See the Custom Features guide for your framework.
- Your own logic: Anything else (fancy interactions, analytics, drag and drop, etc.) is just code you write around the table. There is no black box to fight against.
This also makes it easy to pair TanStack Table with other TanStack libraries:
- TanStack Virtual - Virtualize thousands of rows or columns without changing any table logic (see the Virtualization guide for your framework)
- TanStack Query - Fetch and cache server-side data for manual pagination, sorting, and filtering
- TanStack Form - Build editable cells and inline row editing with validation
- TanStack Pacer - Debounce or throttle filter inputs and other rapid-fire interactions
- TanStack Hotkeys - Add type-safe keyboard shortcuts for table navigation and actions
- TanStack Store - Already powers table state under the hood; provide your own atoms to take full control of any state slice
Get Started
Ready to build something? Head to the Quick Start for your framework, then explore the runnable examples:
React
- React Quick Start
- Kitchen Sink example - Most of the built-in features working together in one table
Using a component library? These examples pair TanStack Table with popular React component libraries, with both a basic version and an advanced (kitchen sink) version for each:
- ShadCN (Base UI): Basic Example | Advanced Example
- ShadCN (Radix UI): Basic Example | Advanced Example
- HeroUI: Basic Example | Advanced Example
- React Aria: Basic Example | Advanced Example
- Material UI: Basic Example | Advanced Example
- Mantine: Basic Example | Advanced Example
- Chakra UI: Basic Example | Advanced Example
Preact
- Preact Quick Start
- Kitchen Sink example - Most of the built-in features working together in one table
Octane
- Octane Quick Start
- Kitchen Sink example - Most of the built-in features working together in one table
Solid
- Solid Quick Start
- Kitchen Sink example - Most of the built-in features working together in one table
Svelte
- Svelte Quick Start
- Kitchen Sink example - Most of the built-in features working together in one table
Using a component library? These examples pair TanStack Table with shadcn-svelte:
- shadcn-svelte: Basic Example | Advanced Example
Vue
- Vue Quick Start
- Kitchen Sink example - Most of the built-in features working together in one table
Using a component library? These examples pair TanStack Table with shadcn-vue:
- shadcn-vue: Basic Example | Advanced Example
Angular
- Angular Quick Start
- Kitchen Sink example - Most of the built-in features working together in one table
Ember
- Ember Quick Start
- Kitchen Sink example - Most of the built-in features working together in one table
Lit
- Lit Quick Start
- Kitchen Sink example - Most of the built-in features working together in one table
Alpine
- Alpine Quick Start
- Basic example - A simple table to get you started
Vanilla
- Vanilla Quick Start
- Basic example - A simple table built directly on
@tanstack/table-core
Reference Index (`docs/reference/index.md`)
id: "@tanstack/table-core" title: "@tanstack/table-core"
Modules
Reference Index Index (`docs/reference/index/index.md`)
id: index title: index
Interfaces
- AggregationContext
- AggregationFnDef
- AggregationFnDescriptor
- AggregationFns
- AggregationMergeContext
- AggregationValueContext
- AggregationValueOptions
- AggregationValueResult
- API
- CachedRowModel_All
- CachedRowModel_Core
- CachedRowModel_Expanded
- CachedRowModel_Faceted
- CachedRowModel_Filtered
- CachedRowModel_Grouped
- CachedRowModel_Paginated
- CachedRowModel_Sorted
- CachedRowModels_FeatureMap
- Cell_Cell
- Cell_CellSelection
- Cell_CellSpanning
- Cell_ColumnGrouping
- Cell_Core
- Cell_CoreProperties
- Cell_FeatureMap
- Cell_RowAggregation
- CellContext
- CellSelectionBounds
- CellSelectionEdges
- CellSelectionRange
- CellSpanIndex
- ColSpanContext
- Column_Column
- Column_ColumnFaceting
- Column_ColumnFiltering
- Column_ColumnGrouping
- Column_ColumnOrdering
- Column_ColumnPinning
- Column_ColumnResizing
- Column_ColumnSizing
- Column_ColumnVisibility
- Column_Core
- Column_CoreProperties
- Column_FeatureMap
- Column_GlobalFiltering
- Column_Internal
- Column_RowAggregation
- Column_RowSorting
- ColumnDef_CellSelection
- ColumnDef_CellSpanning
- ColumnDef_ColumnFiltering
- ColumnDef_ColumnGrouping
- ColumnDef_ColumnPinning
- ColumnDef_ColumnResizing
- ColumnDef_ColumnSizing
- ColumnDef_ColumnVisibility
- ColumnDef_FeatureMap
- ColumnDef_GlobalFiltering
- ColumnDef_RowAggregation
- ColumnDef_RowSorting
- ColumnDefaultOptions
- ColumnFilter
- ColumnHelper
- ColumnIndexes
- ColumnMeta
- ColumnOffsets
- ColumnOffsetsByPosition
- ColumnOrderDefaultOptions
- ColumnPinningDefaultOptions
- ColumnPinningState
- columnResizingState
- ColumnSort
- CoreFeatures
- CreatedFilterFn
- CreatedSortFn
- FeatureSlotPrereqs
- FilterFn
- FilterFnDef
- FilterFns
- FilterMeta
- Header_ColumnResizing
- Header_ColumnSizing
- Header_Core
- Header_CoreProperties
- Header_FeatureMap
- Header_Header
- HeaderContext
- HeaderGroup
- HeaderGroup_Core
- HeaderGroup_Header
- IdIdentifier
- PaginationDefaultOptions
- PaginationState
- Plugins
- PrototypeAPI
- ResolvedAggregationFn
- ResolvedColumnFilter
- Row_ColumnFiltering
- Row_ColumnGrouping
- Row_ColumnPinning
- Row_ColumnVisibility
- Row_Core
- Row_CoreProperties
- Row_FeatureMap
- Row_Row
- Row_RowAggregation
- Row_RowExpanding
- Row_RowPinning
- Row_RowSelection
- RowModel
- RowModelFns_All
- RowModelFns_ColumnFiltering
- RowModelFns_Core
- RowModelFns_FeatureMap
- RowModelFns_RowAggregation
- RowModelFns_RowSorting
- RowPinningDefaultOptions
- RowPinningState
- RowSpanContext
- SelectCellRangeOptions
- SortFn
- SortFnDef
- SortFns
- StockFeatures
- StringHeaderIdentifier
- Table_CellSelection
- Table_CellSpanning
- Table_ColumnFaceting
- Table_ColumnFiltering
- Table_ColumnGrouping
- Table_ColumnOrdering
- Table_ColumnPinning
- Table_ColumnResizing
- Table_Columns
- Table_ColumnSizing
- Table_ColumnVisibility
- Table_Core
- Table_CoreProperties
- Table_FeatureMap
- Table_GlobalFiltering
- Table_Headers
- Table_Internal
- Table_RowAggregation
- Table_RowExpanding
- Table_RowModels_Core
- Table_RowModels_Expanded
- Table_RowModels_Faceted
- Table_RowModels_Filtered
- Table_RowModels_Grouped
- Table_RowModels_Paginated
- Table_RowModels_Sorted
- Table_RowPagination
- Table_RowPinning
- Table_Rows
- Table_RowSelection
- Table_RowSorting
- Table_Table
- TableFeature
- TableFeatures
- TableMeta
- TableOptions_Cell
- TableOptions_CellSelection
- TableOptions_CellSpanning
- TableOptions_ColumnFiltering
- TableOptions_ColumnGrouping
- TableOptions_ColumnOrdering
- TableOptions_ColumnPinning
- TableOptions_ColumnResizing
- TableOptions_Columns
- TableOptions_ColumnSizing
- TableOptions_ColumnVisibility
- TableOptions_Core
- TableOptions_FeatureMap
- TableOptions_GlobalFiltering
- TableOptions_RowAggregation
- TableOptions_RowExpanding
- TableOptions_RowPagination
- TableOptions_RowPinning
- TableOptions_Rows
- TableOptions_RowSelection
- TableOptions_RowSorting
- TableOptions_Table
- TableState_All
- TableState_CellSelection
- TableState_ColumnFiltering
- TableState_ColumnGrouping
- TableState_ColumnOrdering
- TableState_ColumnPinning
- TableState_ColumnResizing
- TableState_ColumnSizing
- TableState_ColumnVisibility
- TableState_FeatureMap
- TableState_GlobalFiltering
- TableState_RowExpanding
- TableState_RowPagination
- TableState_RowPinning
- TableState_RowSelection
- TableState_RowSorting
- ToggleSelectedOptions
Type Aliases
- AccessorColumnDef
- AccessorFn
- AccessorFnColumnDef
- AccessorFnColumnDefBase
- AccessorKeyColumnDef
- AccessorKeyColumnDefBase
- AggregationFnListItem
- AggregationFnOption
- AggregationFnRef
- AggregationResult
- AggregationResultOf
- APIObject
- Atoms
- Atoms_All
- BaseAtoms
- BaseAtoms_All
- BuiltInAggregationFn
- BuiltInFilterFn
- BuiltInSortFn
- CachedRowModels
- Cell
- CellData
- CellSelectionDirection
- CellSelectionRangeMode
- CellSelectionRangeOperation
- CellSelectionState
- Column
- ColumnAggregationValue
- ColumnDef
- ColumnDefBase
- ColumnDefBase_All
- ColumnDefResolved
- ColumnDefTemplate
- ColumnFilterAutoRemoveTestFn
- ColumnFiltersState
- ColumnOrderState
- ColumnPinningPosition
- ColumnResizeDirection
- ColumnResizeMode
- ColumnResizingDefaultOptions
- ColumnSizingDefaultOptions
- ColumnSizingState
- ColumnVisibilityState
- CustomAggregationFns
- CustomFilterFns
- CustomSortFns
- DebugOptions
- DeepKeys
- DeepValue
- DisplayColumnDef
- ExpandedState
- ExpandedStateList
- ExternalAtoms
- ExternalAtoms_All
- ExtractAggregationFnKeys
- ExtractColumnMeta
- ExtractFeatureMapTypes
- ExtractFilterFnKeys
- ExtractFilterMeta
- ExtractSortFnKeys
- ExtractTableMeta
- FilterFnOption
- Getter
- GroupColumnDef
- GroupingColumnMode
- GroupingState
- Header
- IdentifiedColumnDef
- IsAny
- NoInfer
- NonFeatureKeys
- OnChangeFn
- PartialKeys
- Prettify
- PrototypeAPIObject
- RequiredKeys
- Row
- RowData
- RowModelFns
- RowPinningPosition
- RowSelectionState
- SortDirection
- SortFnOption
- SortingState
- StateSliceEqualityFn
- StringOrTemplateHeader
- Table
- Table_RowModels
- TableOptions
- TableOptions_All
- TableState
- TransformDataValueFn
- TransformFilterValueFn
- UnionToIntersection
- Updater
- ValidateFeatureSlots
- VisibilityDefaultOptions
Variables
- aggregationFn_count
- aggregationFn_extent
- aggregationFn_first
- aggregationFn_last
- aggregationFn_max
- aggregationFn_mean
- aggregationFn_median
- aggregationFn_min
- aggregationFn_sum
- aggregationFn_unique
- aggregationFn_uniqueCount
aggregationFns- cellSelectionFeature
- cellSpanningFeature
- columnFacetingFeature
- columnFilteringFeature
- columnGroupingFeature
- columnOrderingFeature
- columnPinningFeature
- columnResizingFeature
- columnSizingFeature
- columnVisibilityFeature
- coreCellsFeature
- coreColumnsFeature
- coreFeatures
- coreHeadersFeature
- coreRowModelsFeature
- coreRowsFeature
- coreTablesFeature
- filterFn_arrHas
- filterFn_arrIncludes
- filterFn_arrIncludesAll
- filterFn_arrIncludesSome
- filterFn_between
- filterFn_betweenInclusive
- filterFn_empty
- filterFn_endsWith
- filterFn_equals
- filterFn_equalsString
- filterFn_equalsStringSensitive
- filterFn_greaterThan
- filterFn_greaterThanOrEqualTo
- filterFn_includesString
- filterFn_includesStringSensitive
- filterFn_inDateRange
- filterFn_inNumberRange
- filterFn_lessThan
- filterFn_lessThanOrEqualTo
- filterFn_notEmpty
- filterFn_startsWith
- filterFn_weakEquals
filterFns- globalFilteringFeature
- reSplitAlphaNumeric
- rowAggregationFeature
- rowExpandingFeature
- rowPaginationFeature
- rowPinningFeature
- rowSelectionFeature
- rowSortingFeature
- sortFn_alphanumeric
- sortFn_alphanumericCaseSensitive
- sortFn_basic
- sortFn_datetime
- sortFn_text
- sortFn_textCaseSensitive
sortFns- stockFeatures
Functions
- assignPrototypeAPIs
- assignTableAPIs
- buildHeaderGroups
- callMemoOrStaticFn
- cloneState
- constructAggregationFn
- constructCell
- constructColumn
- constructFilterFn
- constructHeader
- constructRow
- constructSortFn
- constructTable
- copyInstancePropertiesWithoutMemos
- createColumnHelper
- createCoreRowModel
- createExpandedRowModel
- createFacetedMinMaxValues
- createFacetedRowModel
- createFacetedUniqueValues
- createFilteredRowModel
- createGroupedRowModel
- createPaginatedRowModel
- createSortedRowModel
- expandRows
- flattenBy
- functionalUpdate
- getFunctionNameInfo
- getInitialTableState
- hasOwn
- isFunction
- makeObjectMap
- makeStateUpdater
- memo
- metaHelper
- setStateSlice
- skipFirstRun
- stateSlicesEqual
- tableFeatures
- tableMemo
- tableOptions
Reference Static Functions Index (`docs/reference/static-functions/index.md`)
id: static-functions title: static-functions
Functions
- aggregateColumnValue
- cell_getCanSelect
- cell_getColSpan
- cell_getContext
- cell_getIsAggregated
- cell_getIsCovered
- cell_getIsFocused
- cell_getIsGrouped
- cell_getIsPlaceholder
- cell_getIsSelected
- cell_getRowSpan
- cell_getSelectionEdges
- cell_getSelectionExtendHandler
- cell_getSelectionStartHandler
- cell_getTabIndex
- cell_getValue
- cell_renderValue
- column_clearSorting
- column_getAfter
- column_getAggregationFns
- column_getAggregationValue
- column_getAutoAggregationFn
- column_getAutoFilterFn
- column_getAutoSortDir
- column_getAutoSortFn
- column_getCanFilter
- column_getCanGlobalFilter
- column_getCanGroup
- column_getCanHide
- column_getCanMultiSort
- column_getCanPin
- column_getCanResize
- column_getCanSort
- column_getCanSpan
- column_getFacetedMinMaxValues
- column_getFacetedRowModel
- column_getFacetedUniqueValues
- column_getFilterFn
- column_getFilterIndex
- column_getFilterValue
- column_getFirstSortDir
- column_getFlatColumns
- column_getGroupedIndex
- column_getIndex
- column_getIsFiltered
- column_getIsFirstColumn
- column_getIsGrouped
- column_getIsLastColumn
- column_getIsPinned
- column_getIsResizing
- column_getIsSorted
- column_getIsVisible
- column_getLeafColumns
- column_getNextSortingOrder
- column_getPinnedIndex
- column_getSize
- column_getSortFn
- column_getSortIndex
- column_getStart
- column_getToggleGroupingHandler
- column_getToggleSortingHandler
- column_getToggleVisibilityHandler
- column_pin
- column_resetSize
- column_setFilterValue
- column_toggleGrouping
- column_toggleSorting
- column_toggleVisibility
- formatAggregatedCellValue
- getDefaultCellSelectionState
- getDefaultColumnFiltersState
- getDefaultColumnOrderState
- getDefaultColumnPinningState
- getDefaultColumnResizingState
- getDefaultColumnSizingColumnDef
- getDefaultColumnSizingState
- getDefaultColumnVisibilityState
- getDefaultExpandedState
- getDefaultGroupingState
- getDefaultPaginationState
- getDefaultRowPinningState
- getDefaultRowSelectionState
- getDefaultSortingState
- header_getContext
- header_getLeafHeaders
- header_getResizeHandler
- header_getSize
- header_getStart
- isRowSelected
- isSubRowSelected
- isTouchStartEvent
- normalizeAggregationRows
- normalizeUniqueAggregationRows
- orderColumns
- passiveEventSupported
- row_getAllCells
- row_getAllCellsByColumnId
- row_getCanExpand
- row_getCanMultiSelect
- row_getCanPin
- row_getCanSelect
- row_getCanSelectSubRows
- row_getCenterVisibleCells
- row_getDisplayIndex
- row_getEndVisibleCells
- row_getGroupingValue
- row_getIsAllParentsExpanded
- row_getIsAllSubRowsSelected
- row_getIsExpanded
- row_getIsGrouped
- row_getIsPinned
- row_getIsSelected
- row_getIsSomeSelected
- row_getLeafRows
- row_getParentRow
- row_getParentRows
- row_getPinnedIndex
- row_getStartVisibleCells
- row_getToggleExpandedHandler
- row_getToggleSelectedHandler
- row_getUniqueValues
- row_getValue
- row_getVisibleCells
- row_getVisibleCellsByColumnId
- row_pin
- row_renderValue
- row_toggleExpanded
- row_toggleSelected
- selectRowsFn
- shouldAutoRemoveFilter
- table_autoResetCellSelection
- table_autoResetExpanded
- table_autoResetPageIndex
- table_autoResetSorting
- table_extendCellSelection
- table_firstPage
- table_getAllColumns
- table_getAllFlatColumns
- table_getAllFlatColumnsById
- table_getAllLeafColumns
- table_getAllLeafColumnsById
- table_getBottomRows
- table_getCanLastPage
- table_getCanNextPage
- table_getCanPreviousPage
- table_getCanSomeRowsExpand
- table_getCellSelectionBounds
- table_getCellSelectionColumnIds
- table_getCellSelectionColumnIndexes
- table_getCellSelectionMergeBounds
- table_getCellSelectionRowIds
- table_getCellSpanIndex
- table_getCenterFlatHeaders
- table_getCenterFooterGroups
- table_getCenterHeaderGroups
- table_getCenterLeafColumns
- table_getCenterLeafHeaders
- table_getCenterRows
- table_getCenterTotalSize
- table_getCenterVisibleLeafColumns
- table_getColumn
- table_getColumnIndexes
- table_getColumnOffsets
- table_getCoreRowModel
- table_getDefaultColumnDef
- table_getEndFlatHeaders
- table_getEndFooterGroups
- table_getEndHeaderGroups
- table_getEndLeafColumns
- table_getEndLeafHeaders
- table_getEndTotalSize
- table_getEndVisibleLeafColumns
- table_getExpandedDepth
- table_getExpandedRowModel
- table_getFilteredRowModel
- table_getFilteredSelectedRowModel
- table_getFlatHeaders
- table_getFocusedCell
- table_getFooterGroups
- table_getGlobalAutoFilterFn
- table_getGlobalFacetedMinMaxValues
- table_getGlobalFacetedRowModel
- table_getGlobalFacetedUniqueValues
- table_getGlobalFilterFn
- table_getGroupedRowModel
- table_getGroupedSelectedRowModel
- table_getHeaderGroups
- table_getIsAllColumnsVisible
- table_getIsAllPageRowsSelected
- table_getIsAllRowsExpanded
- table_getIsAllRowsSelected
- table_getIsSomeColumnsPinned
- table_getIsSomeColumnsVisible
- table_getIsSomePageRowsSelected
- table_getIsSomeRowsExpanded
- table_getIsSomeRowsPinned
- table_getIsSomeRowsSelected
- table_getLeafHeaders
- table_getMaxSubRowDepth
- table_getOrderColumnsFn
- table_getPageCount
- table_getPageOptions
- table_getPaginatedRowModel
- table_getPinnedLeafColumns
- table_getPinnedVisibleLeafColumns
- table_getPreExpandedRowModel
- table_getPreFilteredRowModel
- table_getPreGroupedRowModel
- table_getPrePaginatedRowModel
- table_getPreSelectedRowModel
- table_getPreSortedRowModel
- table_getRow
- table_getRowCount
- table_getRowId
- table_getRowModel
- table_getRowsInDisplayOrder
- table_getSelectedCellCount
- table_getSelectedCellIds
- table_getSelectedCellRangesData
- table_getSelectedRowIds
- table_getSelectedRowModel
- table_getSortedRowModel
- table_getStartFlatHeaders
- table_getStartFooterGroups
- table_getStartHeaderGroups
- table_getStartLeafColumns
- table_getStartLeafHeaders
- table_getStartTotalSize
- table_getStartVisibleLeafColumns
- table_getToggleAllColumnsVisibilityHandler
- table_getToggleAllPageRowsSelectedHandler
- table_getToggleAllRowsExpandedHandler
- table_getToggleAllRowsSelectedHandler
- table_getTopRows
- table_getTotalSize
- table_getVisibleFlatColumns
- table_getVisibleLeafColumns
- table_lastPage
- table_mergeOptions
- table_moveCellSelection
- table_nextPage
- table_previousPage
- table_publishExternalState
- table_reset
- table_resetCellSelection
- table_resetColumnFilters
- table_resetColumnOrder
- table_resetColumnPinning
- table_resetColumnSizing
- table_resetColumnVisibility
- table_resetExpanded
- table_resetGlobalFilter
- table_resetGrouping
- table_resetHeaderSizeInfo
- table_resetPageIndex
- table_resetPageSize
- table_resetPagination
- table_resetRowPinning
- table_resetRowSelection
- table_resetSorting
- table_selectAllCells
- table_selectCellRange
- table_setCellSelection
- table_setColumnFilters
- table_setColumnOrder
- table_setColumnPinning
- table_setColumnResizing
- table_setColumnSizing
- table_setColumnVisibility
- table_setExpanded
- table_setFocusedCell
- table_setGlobalFilter
- table_setGrouping
- table_setOptions
- table_setPageIndex
- table_setPageSize
- table_setPagination
- table_setRowPinning
- table_setRowSelection
- table_setSorting
- table_syncExternalStateToBaseAtoms
- table_toggleAllColumnsVisible
- table_toggleAllPageRowsSelected
- table_toggleAllRowsExpanded
- table_toggleAllRowsSelected
Framework Alpine Reference Index (`docs/framework/alpine/reference/index.md`)
id: "@tanstack/alpine-table" title: "@tanstack/alpine-table"
Type Aliases
Functions
Framework Angular Reference Index (`docs/framework/angular/reference/index.md`)
id: "@tanstack/angular-table" title: "@tanstack/angular-table"
Classes
- FlexRenderCell
- FlexRenderComponentInstance
- FlexRenderDirective
- TanStackTable
- TanStackTableCell
- TanStackTableHeader
Interfaces
Type Aliases
- AngularTable
- AppAngularTable
- AppCellContext
- AppColumnDefBase
- AppColumnDefTemplate
- AppColumnHelper
- AppDisplayColumnDef
- AppGroupColumnDef
- AppHeaderContext
- CreateTableContextOptions
- CreateTableHookResult
- FlexRenderComponentProps
- FlexRenderContent
- FlexRenderInputContent
- RenderableComponent
- SubscribeSource
Variables
Functions
- createTableHook
- flexRenderComponent
- injectFlexRenderContext
- injectTable
- injectTableCellContext
- injectTableContext
- injectTableHeaderContext
- shallow
Framework Ember Reference Index (`docs/framework/ember/reference/index.md`)
id: "@tanstack/ember-table" title: "@tanstack/ember-table"
Classes
Interfaces
Type Aliases
Functions
Framework Lit Reference Index (`docs/framework/lit/reference/index.md`)
id: "@tanstack/lit-table" title: "@tanstack/lit-table"
Classes
Interfaces
Type Aliases
- AppCellContext
- AppColumnDefBase
- AppColumnDefTemplate
- AppColumnHelper
- AppDisplayColumnDef
- AppGroupColumnDef
- AppHeaderContext
- AppLitTable
- BoundComponents
- ComponentType
- CreateTableHookOptions
- FlexRenderProps
- LitRenderable
- LitTable
- SelectionSource
Variables
Functions
Framework Octane Reference Index (`docs/framework/octane/reference/index.md`)
id: "@tanstack/octane-table" title: "@tanstack/octane-table"
Interfaces
- AppCellComponent
- AppCellContext
- AppCellPropsWithoutSelector
- AppCellPropsWithSelector
- AppColumnHelper
- AppHeaderComponent
- AppHeaderContext
- AppHeaderPropsWithoutSelector
- AppHeaderPropsWithSelector
- AppTableComponent
- AppTablePropsWithoutSelector
- AppTablePropsWithSelector
- CreateTableHookResult
- SubscribeComponent
- SubscribePropsWithSourceIdentity
- SubscribePropsWithSourceWithSelector
- SubscribePropsWithStore
- TableHookContexts
Type Aliases
- AppColumnDefBase
- AppColumnDefTemplate
- AppDisplayColumnDef
- AppGroupColumnDef
- AppOctaneTable
- CreateTableHookOptions
- FlexRenderProps
- OctaneTable
- Renderable
- SubscribeProps
- SubscribePropsWithSource
- SubscribeSource
- SubscribeStaticChild
- TableComponentType
Variables
Functions
Framework Preact Reference Index (`docs/framework/preact/reference/index.md`)
id: "@tanstack/preact-table" title: "@tanstack/preact-table"
Interfaces
- AppCellComponent
- AppCellPropsWithoutSelector
- AppCellPropsWithSelector
- AppHeaderComponent
- AppHeaderPropsWithoutSelector
- AppHeaderPropsWithSelector
- AppTableComponent
- AppTablePropsWithoutSelector
- AppTablePropsWithSelector
- CreateTableHookResult
- TableHookContexts
Type Aliases
- AppCellContext
- AppColumnDefBase
- AppColumnDefTemplate
- AppColumnHelper
- AppDisplayColumnDef
- AppGroupColumnDef
- AppHeaderContext
- AppPreactTable
- CreateTableHookOptions
- FlexRenderProps
- PreactTable
- Renderable
- SubscribeProps
- SubscribePropsWithSource
- SubscribePropsWithSourceIdentity
- SubscribePropsWithSourceWithSelector
- SubscribePropsWithStore
- SubscribeSource
Functions
Framework React Reference Index (`docs/framework/react/reference/index.md`)
id: "@tanstack/react-table" title: "@tanstack/react-table"
Modules
Framework Solid Reference Index (`docs/framework/solid/reference/index.md`)
id: "@tanstack/solid-table" title: "@tanstack/solid-table"
Interfaces
- AppCellComponent
- AppCellProps
- AppHeaderComponent
- AppHeaderProps
- AppTableComponent
- AppTableProps
- CreateTableHookResult
Type Aliases
- AppCellContext
- AppColumnDefBase
- AppColumnDefTemplate
- AppColumnHelper
- AppDisplayColumnDef
- AppGroupColumnDef
- AppHeaderContext
- AppSolidTable
- ComponentType
- CreateTableHookOptions
- FlexRenderProps
- SolidTable
Functions
Framework Svelte Reference Index (`docs/framework/svelte/reference/index.md`)
id: "@tanstack/svelte-table" title: "@tanstack/svelte-table"
Interfaces
Type Aliases
- AppCellContext
- AppColumnDefBase
- AppColumnDefTemplate
- AppColumnHelper
- AppDisplayColumnDef
- AppGroupColumnDef
- AppHeaderContext
- AppSvelteTable
- ComponentType
- CreateTableHookOptions
- FlexRender
- SvelteTable
Variables
Functions
Framework Vue Reference Index (`docs/framework/vue/reference/index.md`)
id: "@tanstack/vue-table" title: "@tanstack/vue-table"
Interfaces
Type Aliases
- AppCellContext
- AppColumnDefBase
- AppColumnDefTemplate
- AppColumnHelper
- AppDisplayColumnDef
- AppGroupColumnDef
- AppHeaderContext
- AppVueTable
- ComponentType
- CreateTableHookOptions
- TableOptionsWithReactiveData
- VueTable
Variables
Functions
Reference Index Interfaces CellSpanIndex (`docs/reference/index/interfaces/CellSpanIndex.md`)
id: CellSpanIndex title: CellSpanIndex
Defined in: features/cell-spanning/cellSpanningFeature.types.ts:13
The table's cell span index for the rows that are currently rendered.
Cell APIs read this; it is exposed for devtools and for virtualizers that
need to know where a run's anchor sits relative to the rendered window.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
Properties
colSpans
colSpans: (Int32Array<ArrayBufferLike> | undefined)[];Defined in: features/cell-spanning/cellSpanningFeature.types.ts:23
Column spans per render-order row position, indexed by render-order column
position. Holes are the common case: only rows that declared a column span
get an array. In a stored array, the spanning cell holds its span and the
cells it covers hold 0; every other cell holds 1.
columnIndexes
columnIndexes: Record<string, number>;Defined in: features/cell-spanning/cellSpanningFeature.types.ts:27
Render-order index for every visible column id. Hidden columns are absent.
rows
rows: readonly Row<TFeatures, TData>[];Defined in: features/cell-spanning/cellSpanningFeature.types.ts:40
The exact ordered rows this index was built from, in render order:
top-pinned rows, then the (paginated) center rows, then bottom-pinned
rows. Cell reads compare against this to reject stale row positions.
rowSpans
rowSpans: Record<string, Int32Array>;Defined in: features/cell-spanning/cellSpanningFeature.types.ts:34
Row spans per column id, indexed by render-order row position. Only columns
that produced at least one run longer than one row appear here; a missing
column means every cell in it spans exactly one row. In a stored array, the
run's anchor row holds the run length and the rows it covers hold 0.
Reference Static Functions Functions Cell GetTabIndex (`docs/reference/static-functions/functions/cell_getTabIndex.md`)
id: cell_getTabIndex title: cell_getTabIndex
function cell_getTabIndex<TFeatures, TData, TValue>(cell): number;Defined in: features/cell-selection/cellSelectionFeature.utils.ts:605
Returns 0 for the focused cell and -1 otherwise, for roving tabindex.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
TValue
TValue extends unknown = unknown
Parameters
cell
Cell<TFeatures, TData, TValue>
Returns
number
Example
const tabIndex = cell_getTabIndex(cell)Reference Static Functions Functions Column GetFilterIndex (`docs/reference/static-functions/functions/column_getFilterIndex.md`)
id: column_getFilterIndex title: column_getFilterIndex
function column_getFilterIndex<TFeatures, TData, TValue>(column): number;Defined in: features/column-filtering/columnFilteringFeature.utils.ts:211
Finds this column's position in the ordered state.columnFilters array.
The result is -1 when the column has no active filter.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
TValue
TValue extends unknown = unknown
Parameters
column
Column_Internal<TFeatures, TData, TValue>
Returns
number
Example
const index = column_getFilterIndex(column)Reference Static Functions Functions Column GetGroupedIndex (`docs/reference/static-functions/functions/column_getGroupedIndex.md`)
id: column_getGroupedIndex title: column_getGroupedIndex
function column_getGroupedIndex<TFeatures, TData, TValue>(column): number;Defined in: features/column-grouping/columnGroupingFeature.utils.ts:106
Finds this column's position in the ordered grouping state.
The result is -1 when the column is not grouped.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
TValue
TValue extends unknown = unknown
Parameters
column
Column_Internal<TFeatures, TData, TValue>
Returns
number
Example
const index = column_getGroupedIndex(column)Reference Static Functions Functions Column GetIndex (`docs/reference/static-functions/functions/column_getIndex.md`)
id: column_getIndex title: column_getIndex
function column_getIndex<TFeatures, TData, TValue>(column, position?): number;Defined in: features/column-ordering/columnOrderingFeature.utils.ts:81
Finds this column's index within a visible pinning region.
Pass 'start', 'center', or 'end' to search that region; omit the
position to search the full visible leaf column list.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
TValue
TValue extends unknown = unknown
Parameters
column
Column_Internal<TFeatures, TData, TValue>
position?
| ColumnPinningPosition
| "center"
Returns
number
Example
const index = column_getIndex(column, 'center')Reference Static Functions Functions Column GetPinnedIndex (`docs/reference/static-functions/functions/column_getPinnedIndex.md`)
id: column_getPinnedIndex title: column_getPinnedIndex
function column_getPinnedIndex<TFeatures, TData, TValue>(column): number;Defined in: features/column-pinning/columnPinningFeature.utils.ts:179
Finds this column's index within its pinned region.
Unpinned columns return 0; pinned columns return their position instate.columnPinning.start or state.columnPinning.end.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
TValue
TValue extends unknown = unknown
Parameters
column
Column_Internal<TFeatures, TData, TValue>
Returns
number
Example
const index = column_getPinnedIndex(column)Reference Static Functions Functions Column GetSortIndex (`docs/reference/static-functions/functions/column_getSortIndex.md`)
id: column_getSortIndex title: column_getSortIndex
function column_getSortIndex<TFeatures, TData, TValue>(column): number;Defined in: features/row-sorting/rowSortingFeature.utils.ts:491
Finds this column's position in the ordered state.sorting array.
The result is -1 when the column is not sorted.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
TValue
TValue extends unknown = unknown
Parameters
column
Column_Internal<TFeatures, TData, TValue>
Returns
number
Example
const index = column_getSortIndex(column)Reference Static Functions Functions Row GetDisplayIndex (`docs/reference/static-functions/functions/row_getDisplayIndex.md`)
id: row_getDisplayIndex title: row_getDisplayIndex
function row_getDisplayIndex<TFeatures, TData>(row): number;Defined in: core/rows/coreRowsFeature.utils.ts:14
Returns this row's zero-based position in the current pre-pagination row
model. Rows outside that model return -1.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
Parameters
row
Row<TFeatures, TData>
Returns
number
Reference Static Functions Functions Row GetPinnedIndex (`docs/reference/static-functions/functions/row_getPinnedIndex.md`)
id: row_getPinnedIndex title: row_getPinnedIndex
function row_getPinnedIndex<TFeatures, TData>(row): number;Defined in: features/row-pinning/rowPinningFeature.utils.ts:260
Finds this row's visible index within its pinned region.
Unpinned rows return -1.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
Parameters
row
Row<TFeatures, TData>
Returns
number
Example
const index = row_getPinnedIndex(row)Reference Static Functions Functions Table AutoResetPageIndex (`docs/reference/static-functions/functions/table_autoResetPageIndex.md`)
id: table_autoResetPageIndex title: table_autoResetPageIndex
function table_autoResetPageIndex<TFeatures, TData>(table): void;Defined in: features/row-pagination/rowPaginationFeature.utils.ts:40
Resets the page index when a page-altering change should return to page 0.
The reset runs when autoResetAll, autoResetPageIndex, or the default
client-side pagination behavior allows it. Manual pagination opts out unless
the reset options explicitly opt back in.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
Parameters
table
Table_Internal<TFeatures, TData>
Returns
void
Example
table_autoResetPageIndex(table)Reference Static Functions Functions Table GetCellSpanIndex (`docs/reference/static-functions/functions/table_getCellSpanIndex.md`)
id: table_getCellSpanIndex title: table_getCellSpanIndex
function table_getCellSpanIndex<TFeatures, TData>(table): CellSpanIndex<TFeatures, TData>;Defined in: features/cell-spanning/cellSpanningFeature.utils.ts:152
Builds the table's cell span index for the rows that are currently rendered.
Spans are always derived from scratch from the final row model, so sorting,
filtering, pagination, expansion, and row pinning only change adjacency and
the index follows. Nothing is persisted and there is nothing to configure.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
Parameters
table
Table_Internal<TFeatures, TData>
Returns
CellSpanIndex<TFeatures, TData>
Example
const spanIndex = table_getCellSpanIndex(table)Reference Static Functions Functions Table ResetPageIndex (`docs/reference/static-functions/functions/table_resetPageIndex.md`)
id: table_resetPageIndex title: table_resetPageIndex
function table_resetPageIndex<TFeatures, TData>(table, defaultState?): void;Defined in: features/row-pagination/rowPaginationFeature.utils.ts:151
Resets only pagination.pageIndex.
With no argument, the reset uses table.initialState.pagination?.pageIndex
or 0. Passing true always resets the page index to 0.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
Parameters
table
Table_Internal<TFeatures, TData>
defaultState?
boolean
Returns
void
Example
table_resetPageIndex(table)
table_resetPageIndex(table, true)Reference Static Functions Functions Table SetPageIndex (`docs/reference/static-functions/functions/table_setPageIndex.md`)
id: table_setPageIndex title: table_setPageIndex
function table_setPageIndex<TFeatures, TData>(table, updater): void;Defined in: features/row-pagination/rowPaginationFeature.utils.ts:117
Updates pagination.pageIndex and clamps it to the known page range.
Unknown page counts (undefined or -1) allow any non-negative page index.
Known page counts clamp the index between 0 and pageCount - 1.
Type Parameters
TFeatures
TFeatures extends TableFeatures
TData
TData extends RowData
Parameters
table
Table_Internal<TFeatures, TData>
updater
Updater<number>
Returns
void
Example
table_setPageIndex(table, (old) => old)Framework React Reference Index Index (`docs/framework/react/reference/index/index.md`)
id: index title: index
Interfaces
- AppCellComponent
- AppCellPropsWithoutSelector
- AppCellPropsWithSelector
- AppHeaderComponent
- AppHeaderPropsWithoutSelector
- AppHeaderPropsWithSelector
- AppTableComponent
- AppTablePropsWithoutSelector
- AppTablePropsWithSelector
- CreateTableHookResult
- TableHookContexts
Type Aliases
- AppCellContext
- AppColumnDefBase
- AppColumnDefTemplate
- AppColumnHelper
- AppDisplayColumnDef
- AppGroupColumnDef
- AppHeaderContext
- AppReactTable
- CreateTableHookOptions
- FlexRenderProps
- ReactTable
- Renderable
- SubscribeProps
- SubscribePropsWithSource
- SubscribePropsWithSourceIdentity
- SubscribePropsWithSourceWithSelector
- SubscribePropsWithStore
- SubscribeSource
Functions
Framework React Reference Legacy Index (`docs/framework/react/reference/legacy/index.md`)
id: legacy title: legacy
Interfaces
Type Aliases
- FacetedMinMaxValuesFactory
- FacetedRowModelFactory
- FacetedUniqueValuesFactory
LegacyCellLegacyColumnLegacyColumnDefLegacyHeaderLegacyHeaderGroupLegacyReactTableLegacyRowLegacyTableLegacyTableOptions- RowModelFactory
Functions
getCoreRowModelgetExpandedRowModelgetFacetedMinMaxValuesgetFacetedRowModelgetFacetedUniqueValuesgetFilteredRowModelgetGroupedRowModelgetPaginationRowModelgetSortedRowModellegacyCreateColumnHelperuseLegacyTable
Agent Skills (`docs/agent-skills.md`)
title: Agent Skills (TanStack Intent)
id: agent-skills
keywords:
- tanstack table
- tanstack intent
- agent skills
- claude code
- cursor
- github copilot
- codex
- ai coding agents
- SKILL.md
- AGENTS.md
You're building with TanStack Table and using an AI coding agent such as Claude Code, Cursor, GitHub Copilot, or Codex. The agent keeps suggesting v8 APIs such as useReactTable, configures row models without explicit features, or renders with adapter patterns that no longer match v9. By the end of this guide, your agent will load TanStack Table's bundled skills automatically whenever you work on table code, and those skills will stay in sync with whichever TanStack Table version your project installs.
What are Agent Skills?
Agent Skills are markdown documents (SKILL.md) that ship inside npm packages and tell AI coding agents how to use a library correctly: which functions to use, which patterns to avoid, and when to reach for a particular feature. The format is an open standard supported by Claude Code, Cursor, GitHub Copilot, Codex, and others.
TanStack Table publishes skills inside its packages so the guidance travels with npm update instead of being pinned in a model's training data or copied into an agent configuration file manually.
Skills Shipped by TanStack Table
The skills available to your agent depend on which packages your project installs:
| Package | Skills | What they teach |
|---|---|---|
@tanstack/table-core |
core, table-features, table-state, custom-features, migrate-v8-to-v9 |
Core architecture, feature registration, shared state ownership, plugin authoring, and migration. Each optional feature has an on-demand reference linked from table-features. |
@tanstack/<framework>-table |
getting-started, table-state, and migration where supported |
Framework setup, rendering, and reactive state. References cover reusable app hooks, advanced reactivity, and maintained Query/Virtual integrations. |
@tanstack/table-devtools and framework devtools adapters |
devtools |
Registering table instances and inspecting features, state, options, rows, and columns |
@tanstack/match-sorter-utils |
fuzzy-ranking |
Fuzzy filtering, ranking metadata, and rank-aware sorting |
Each skill lives under node_modules//skills/<skill-name>/SKILL.md once the package is installed. Its references/ directory contains guidance for specific tasks. For example, row selection lives in @tanstack/table-core/skills/table-features/references/row-selection.md.
The entry point tells the agent when to read each reference. A sorting change loads the sorting guidance; selection, resizing, and other unrelated feature references stay unloaded. Framework state skills load the shared state model and then explain their own reactive reads and updates.
Intent lists entry points and loads one requested SKILL.md at a time. The agent follows its prerequisite instructions and reads relevant references. A React project with core installed has eight Table entry points. Devtools and other installed libraries add their own entries.
Step 1: Install TanStack Table
If you haven't already, install the adapter for your framework. See Installation for the full package list.
pnpm add @tanstack/react-tableFramework adapters install @tanstack/table-core as a dependency, so both the framework-specific and core skills are available to the installer.
Step 2: Run `intent install`
From the root of your project, run:
npx @tanstack/intent@latest installThe CLI writes lightweight skill-loading guidance into your agent's config file. That guidance tells the agent to discover skills from the packages installed in your project and load the most relevant one before it starts a substantial task.
By default the guidance lands in AGENTS.md. The CLI can also update:
CLAUDE.mdfor Claude Code.cursorrulesfor Cursor.github/copilot-instructions.mdfor GitHub Copilot
Step 3: Review the Generated Guidance
The install command appends (or creates) an intent-skills block that looks like this:
## Skill Loading
Before editing files for a substantial task:
- Run `npx @tanstack/intent@latest list` from the workspace root to see available local skills.
- If a listed skill matches the task, run `npx @tanstack/intent@latest load <package>#<skill>` before changing files.
- Use the loaded `SKILL.md` guidance while making the change.
- Monorepos: when working across packages, run the skill check from the workspace root and prefer the local skill for the package being changed.
- Multiple matches: prefer the most specific local skill for the package or concern you are changing; load additional skills only when the task spans multiple packages or concerns.Keep the block near the top of the config file so your agent sees it before task-specific instructions.
You can inspect and load Table skills yourself with the same commands:
npx @tanstack/intent@latest list
npx @tanstack/intent@latest load @tanstack/react-table#getting-started
npx @tanstack/intent@latest load @tanstack/table-core#table-featuresFor sorting, follow the loaded entry point's link to references/sorting.md. The load command resolves relative Markdown links to the installed package, including when your package manager uses nested dependency paths.
If you prefer explicit task-to-skill entries, run npx @tanstack/intent@latest install --map. Mapping mode scans your installed intent-enabled packages and writes compact id, run, and for entries into the managed block.
Step 4: Confirm It's Wired Up
Open a fresh session in your coding agent and ask it to build something with TanStack Table, for example: "Build a sortable, paginated React table with TanStack Table v9."
You should see:
- The agent uses
useTable()instead of the v8useReactTable()API. - Features and row-model slots are declared explicitly with
tableFeatures(). - Static data, columns, and features keep stable references.
- The adapter's current rendering APIs are used instead of copied v8 rendering patterns.
- Table owns the headless model and state while your application owns markup, styles, interactions, and accessibility.
If the agent still falls back to v8 patterns, reopen its config file and confirm the intent-skills block is present. You can also run intent list to confirm that the installed Table packages are detected and intent load to inspect the matching guidance directly.
Keeping Skills Current
Skills are versioned with each package. When you update your TanStack Table packages, the SKILL.md files under node_modules update with them. No CLI rerun is needed. If you use explicit mappings, rerun npx @tanstack/intent@latest install --map after adding another intent-enabled package, such as a Table devtools adapter, or when you want to refresh the mappings.
Update older skill mappings
The progressive-disclosure layout replaces individual feature and composition skill IDs with references. If your agent configuration contains the old IDs, update them as follows:
| Old skill ID | Replacement entry point and reference |
|---|---|
@tanstack/table-core#sorting, #row-selection, and the other optional feature skills |
@tanstack/table-core#table-features, then the matching feature reference |
@tanstack/table-core#client-vs-server |
@tanstack/table-core#table-features, then references/client-vs-server.md |
@tanstack/table-core#typescript or #api-not-found |
@tanstack/table-core#core, then the matching reference |
@tanstack/<framework>-table#create-table-hook |
The adapter's getting-started, then references/create-table-hook.md |
@tanstack/<framework>-table#with-tanstack-query or #with-tanstack-virtual |
The adapter's getting-started, then the matching integration reference |
If package.json#intent.skills allows individual old skill IDs, run npx @tanstack/intent@latest install --review to select their replacements and the prerequisites they need. Package-level permissions include new entry points automatically. Regenerate explicit mappings with npx @tanstack/intent@latest install --map after updating permissions. Direct file pointers to moved skills also need their new paths.
Core state guidance is available as @tanstack/table-core#table-state. Adapter state and migration skill IDs remain available. Migration skills retain a complete audit checklist and link to detailed mappings for the APIs your project uses.
Using Skills Without the CLI
If you'd rather wire skills in yourself, reference them directly from node_modules in any agent config file. The minimum your agent needs is a pointer to the relevant file:
When working on TanStack React Table code, read and follow:
node_modules/@tanstack/react-table/skills/getting-started/SKILL.mdThe CLI is recommended because it discovers installed packages automatically and stays consistent with the Agent Skills standard, but the underlying file paths are stable.
Learn More
- TanStack Intent documentation, the CLI's full reference, including
scaffold,validate, and CI setup for library maintainers. - Agent Skills registry, where you can browse other intent-enabled packages.
Config.Json (`docs/config.json`)
{
"$schema": "https://raw.githubusercontent.com/TanStack/tanstack.com/main/tanstack-docs-config.schema.json",
"docSearch": {
"appId": "74SF5EKVW9",
"apiKey": "9fc015a3310be6669ed66c6c459f319f",
"indexName": "tanstack-table"
},
"sections": [
{
"label": "Getting Started",
"children": [
{ "label": "Overview", "to": "overview" },
{ "label": "Installation", "to": "installation" },
{ "label": "Devtools", "to": "devtools" },
{ "label": "Agent Skills (TanStack Intent)", "to": "agent-skills" }
],
"frameworks": [
{
"label": "alpine",
"children": [
{ "label": "Quick Start", "to": "framework/alpine/quick-start" }
]
},
{
"label": "angular",
"children": [
{ "label": "Quick Start", "to": "framework/angular/quick-start" },
{ "label": "Migrating to V9", "to": "framework/angular/guide/migrating" }
]
},
{
"label": "ember",
"children": [
{ "label": "Quick Start", "to": "framework/ember/quick-start" }
]
},
{
"label": "lit",
"children": [
{ "label": "Quick Start", "to": "framework/lit/quick-start" },
{ "label": "Migrating to V9", "to": "framework/lit/guide/migrating" }
]
},
{
"label": "react",
"children": [
{ "label": "Quick Start", "to": "framework/react/quick-start" },
{ "label": "Migrating to V9", "to": "framework/react/guide/migrating" },
{ "label": "useLegacyTable Guide", "to": "framework/react/guide/use-legacy-table" }
]
},
{
"label": "preact",
"children": [
{ "label": "Quick Start", "to": "framework/preact/quick-start" },
{ "label": "Migrating to V9", "to": "framework/preact/guide/migrating" }
]
},
{
"label": "octane",
"children": [
{ "label": "Quick Start", "to": "framework/octane/quick-start" }
]
},
{
"label": "solid",
"children": [
{ "label": "Quick Start", "to": "framework/solid/quick-start" },
{ "label": "Migrating to V9", "to": "framework/solid/guide/migrating" }
]
},
{
"label": "svelte",
"children": [
{ "label": "Quick Start", "to": "framework/svelte/quick-start" },
{ "label": "Migrating to V9", "to": "framework/svelte/guide/migrating" }
]
},
{
"label": "vue",
"children": [
{ "label": "Quick Start", "to": "framework/vue/quick-start" },
{ "label": "Migrating to V9", "to": "framework/vue/guide/migrating" }
]
},
{
"label": "vanilla",
"children": [
{ "label": "Quick Start", "to": "framework/vanilla/quick-start" }
]
}
]
},
{
"label": "Core Guides",
"children": [
{ "label": "Features", "to": "guide/features" },
{ "label": "Data", "to": "guide/data" },
{ "label": "Client-Side vs Server-Side", "to": "guide/client-side-vs-server-side" },
{ "label": "Column Definitions", "to": "guide/column-defs" },
{ "label": "Table Instance", "to": "guide/tables" },
{ "label": "Row Models", "to": "guide/row-models" },
{ "label": "Worker Row Models (Experimental)", "to": "guide/worker-row-models" },
{ "label": "Rows", "to": "guide/rows" },
{ "label": "Cells", "to": "guide/cells" },
{ "label": "Header Groups", "to": "guide/header-groups" },
{ "label": "Headers", "to": "guide/headers" },
{ "label": "Columns", "to": "guide/columns" },
{ "label": "Table and Column Meta", "to": "guide/table-and-column-meta" },
{ "label": "Type Helpers", "to": "guide/helpers" }
],
"frameworks": [
{
"label": "alpine",
"children": [
{
"label": "Table State",
"to": "framework/alpine/guide/table-state"
},
{
"label": "Composable Tables (createTableHook)",
"to": "framework/alpine/guide/composable-tables"
},
{ "label": "FlexRender", "to": "framework/alpine/guide/flex-render" },
{
"label": "Custom Plugins",
"to": "framework/alpine/guide/custom-features"
}
]
},
{
"label": "angular",
"children": [
{ "label": "Table State", "to": "framework/angular/guide/table-state" },
{ "label": "Composable Tables (createTableHook)", "to": "framework/angular/guide/composable-tables" },
{ "label": "FlexRender", "to": "framework/angular/guide/flex-render" },
{ "label": "Custom Plugins", "to": "framework/angular/guide/custom-features" }
]
},
{
"label": "ember",
"children": [
{ "label": "Table State", "to": "framework/ember/guide/table-state" },
{ "label": "Composable Tables (createTableHook)", "to": "framework/ember/guide/composable-tables" },
{ "label": "FlexRender", "to": "framework/ember/guide/flex-render" },
{ "label": "Custom Plugins", "to": "framework/ember/guide/custom-features" }
]
},
{
"label": "lit",
"children": [
{ "label": "Table State", "to": "framework/lit/guide/table-state" },
{ "label": "Composable Tables (createTableHook)", "to": "framework/lit/guide/composable-tables" },
{ "label": "FlexRender", "to": "framework/lit/guide/flex-render" },
{ "label": "Custom Plugins", "to": "framework/lit/guide/custom-features" }
]
},
{
"label": "react",
"children": [
{ "label": "Table State", "to": "framework/react/guide/table-state" },
{ "label": "React Compiler", "to": "framework/react/guide/react-compiler" },
{ "label": "Composable Tables (createTableHook)", "to": "framework/react/guide/composable-tables" },
{ "label": "Table Context", "to": "framework/react/guide/table-context" },
{ "label": "FlexRender", "to": "framework/react/guide/flex-render" },
{ "label": "Custom Plugins", "to": "framework/react/guide/custom-features" }
]
},
{
"label": "preact",
"children": [
{ "label": "Table State", "to": "framework/preact/guide/table-state" },
{ "label": "Composable Tables (createTableHook)", "to": "framework/preact/guide/composable-tables" },
{ "label": "Table Context", "to": "framework/preact/guide/table-context" },
{ "label": "FlexRender", "to": "framework/preact/guide/flex-render" },
{ "label": "Custom Plugins", "to": "framework/preact/guide/custom-features" }
]
},
{
"label": "octane",
"children": [
{ "label": "Table State", "to": "framework/octane/guide/table-state" },
{ "label": "Composable Tables (createTableHook)", "to": "framework/octane/guide/composable-tables" },
{ "label": "Table Context", "to": "framework/octane/guide/table-context" },
{ "label": "FlexRender", "to": "framework/octane/guide/flex-render" },
{ "label": "Custom Plugins", "to": "framework/octane/guide/custom-features" }
]
},
{
"label": "solid",
"children": [
{ "label": "Table State", "to": "framework/solid/guide/table-state" },
{ "label": "Composable Tables (createTableHook)", "to": "framework/solid/guide/composable-tables" },
{ "label": "FlexRender", "to": "framework/solid/guide/flex-render" },
{ "label": "Custom Plugins", "to": "framework/solid/guide/custom-features" }
]
},
{
"label": "svelte",
"children": [
{ "label": "Table State", "to": "framework/svelte/guide/table-state" },
{ "label": "Composable Tables (createTableHook)", "to": "framework/svelte/guide/composable-tables" },
{ "label": "FlexRender", "to": "framework/svelte/guide/flex-render" },
{ "label": "Custom Plugins", "to": "framework/svelte/guide/custom-features" }
]
},
{
"label": "vue",
"children": [
{ "label": "Table State", "to": "framework/vue/guide/table-state" },
{ "label": "Composable Tables (createTableHook)", "to": "framework/vue/guide/composable-tables" },
{ "label": "FlexRender", "to": "framework/vue/guide/flex-render" },
{ "label": "Custom Plugins", "to": "framework/vue/guide/custom-features" }
]
},
{
"label": "vanilla",
"children": [
{ "label": "Table State", "to": "framework/vanilla/guide/table-state" },
{ "label": "FlexRender", "to": "framework/vanilla/guide/flex-render" }
]
}
]
},
{
"label": "Feature Guides",
"children": [],
"frameworks": [
{
"label": "alpine",
"children": [
{ "label": "Cell Selection", "to": "framework/alpine/guide/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/alpine/guide/cell-spanning" },
{ "label": "Column Ordering", "to": "framework/alpine/guide/column-ordering" },
{ "label": "Column Pinning", "to": "framework/alpine/guide/column-pinning" },
{ "label": "Column Sizing", "to": "framework/alpine/guide/column-sizing" },
{ "label": "Column Resizing", "to": "framework/alpine/guide/column-resizing" },
{ "label": "Column Visibility", "to": "framework/alpine/guide/column-visibility" },
{ "label": "Column Filtering", "to": "framework/alpine/guide/column-filtering" },
{ "label": "Global Filtering", "to": "framework/alpine/guide/global-filtering" },
{ "label": "Fuzzy Filtering", "to": "framework/alpine/guide/fuzzy-filtering" },
{ "label": "Faceting", "to": "framework/alpine/guide/column-faceting" },
{ "label": "Aggregation", "to": "framework/alpine/guide/aggregation" },
{ "label": "Grouping", "to": "framework/alpine/guide/grouping" },
{ "label": "Expanding", "to": "framework/alpine/guide/expanding" },
{ "label": "Pagination", "to": "framework/alpine/guide/pagination" },
{ "label": "Row Pinning", "to": "framework/alpine/guide/row-pinning" },
{ "label": "Row Selection", "to": "framework/alpine/guide/row-selection" },
{ "label": "Sorting", "to": "framework/alpine/guide/sorting" }
]
},
{
"label": "angular",
"children": [
{ "label": "Cell Selection", "to": "framework/angular/guide/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/angular/guide/cell-spanning" },
{ "label": "Column Ordering", "to": "framework/angular/guide/column-ordering" },
{ "label": "Column Pinning", "to": "framework/angular/guide/column-pinning" },
{ "label": "Column Sizing", "to": "framework/angular/guide/column-sizing" },
{ "label": "Column Resizing", "to": "framework/angular/guide/column-resizing" },
{ "label": "Column Visibility", "to": "framework/angular/guide/column-visibility" },
{ "label": "Column Filtering", "to": "framework/angular/guide/column-filtering" },
{ "label": "Global Filtering", "to": "framework/angular/guide/global-filtering" },
{ "label": "Fuzzy Filtering", "to": "framework/angular/guide/fuzzy-filtering" },
{ "label": "Faceting", "to": "framework/angular/guide/column-faceting" },
{ "label": "Aggregation", "to": "framework/angular/guide/aggregation" },
{ "label": "Grouping", "to": "framework/angular/guide/grouping" },
{ "label": "Expanding", "to": "framework/angular/guide/expanding" },
{ "label": "Pagination", "to": "framework/angular/guide/pagination" },
{ "label": "Row Pinning", "to": "framework/angular/guide/row-pinning" },
{ "label": "Row Selection", "to": "framework/angular/guide/row-selection" },
{ "label": "Sorting", "to": "framework/angular/guide/sorting" },
{ "label": "Virtualization", "to": "framework/angular/guide/virtualization" }
]
},
{
"label": "ember",
"children": [
{ "label": "Cell Selection", "to": "framework/ember/guide/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/ember/guide/cell-spanning" },
{ "label": "Column Ordering", "to": "framework/ember/guide/column-ordering" },
{ "label": "Column Pinning", "to": "framework/ember/guide/column-pinning" },
{ "label": "Column Sizing", "to": "framework/ember/guide/column-sizing" },
{ "label": "Column Resizing", "to": "framework/ember/guide/column-resizing" },
{ "label": "Column Visibility", "to": "framework/ember/guide/column-visibility" },
{ "label": "Column Filtering", "to": "framework/ember/guide/column-filtering" },
{ "label": "Global Filtering", "to": "framework/ember/guide/global-filtering" },
{ "label": "Fuzzy Filtering", "to": "framework/ember/guide/fuzzy-filtering" },
{ "label": "Faceting", "to": "framework/ember/guide/column-faceting" },
{ "label": "Aggregation", "to": "framework/ember/guide/aggregation" },
{ "label": "Grouping", "to": "framework/ember/guide/grouping" },
{ "label": "Expanding", "to": "framework/ember/guide/expanding" },
{ "label": "Pagination", "to": "framework/ember/guide/pagination" },
{ "label": "Row Pinning", "to": "framework/ember/guide/row-pinning" },
{ "label": "Row Selection", "to": "framework/ember/guide/row-selection" },
{ "label": "Sorting", "to": "framework/ember/guide/sorting" }
]
},
{
"label": "lit",
"children": [
{ "label": "Cell Selection", "to": "framework/lit/guide/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/lit/guide/cell-spanning" },
{ "label": "Column Ordering", "to": "framework/lit/guide/column-ordering" },
{ "label": "Column Pinning", "to": "framework/lit/guide/column-pinning" },
{ "label": "Column Sizing", "to": "framework/lit/guide/column-sizing" },
{ "label": "Column Resizing", "to": "framework/lit/guide/column-resizing" },
{ "label": "Column Visibility", "to": "framework/lit/guide/column-visibility" },
{ "label": "Column Filtering", "to": "framework/lit/guide/column-filtering" },
{ "label": "Global Filtering", "to": "framework/lit/guide/global-filtering" },
{ "label": "Fuzzy Filtering", "to": "framework/lit/guide/fuzzy-filtering" },
{ "label": "Faceting", "to": "framework/lit/guide/column-faceting" },
{ "label": "Aggregation", "to": "framework/lit/guide/aggregation" },
{ "label": "Grouping", "to": "framework/lit/guide/grouping" },
{ "label": "Expanding", "to": "framework/lit/guide/expanding" },
{ "label": "Pagination", "to": "framework/lit/guide/pagination" },
{ "label": "Row Pinning", "to": "framework/lit/guide/row-pinning" },
{ "label": "Row Selection", "to": "framework/lit/guide/row-selection" },
{ "label": "Sorting", "to": "framework/lit/guide/sorting" },
{ "label": "Virtualization", "to": "framework/lit/guide/virtualization" }
]
},
{
"label": "react",
"children": [
{ "label": "Cell Selection", "to": "framework/react/guide/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/react/guide/cell-spanning" },
{ "label": "Column Ordering", "to": "framework/react/guide/column-ordering" },
{ "label": "Column Pinning", "to": "framework/react/guide/column-pinning" },
{ "label": "Column Sizing", "to": "framework/react/guide/column-sizing" },
{ "label": "Column Resizing", "to": "framework/react/guide/column-resizing" },
{ "label": "Column Visibility", "to": "framework/react/guide/column-visibility" },
{ "label": "Column Filtering", "to": "framework/react/guide/column-filtering" },
{ "label": "Global Filtering", "to": "framework/react/guide/global-filtering" },
{ "label": "Fuzzy Filtering", "to": "framework/react/guide/fuzzy-filtering" },
{ "label": "Faceting", "to": "framework/react/guide/column-faceting" },
{ "label": "Aggregation", "to": "framework/react/guide/aggregation" },
{ "label": "Grouping", "to": "framework/react/guide/grouping" },
{ "label": "Expanding", "to": "framework/react/guide/expanding" },
{ "label": "Pagination", "to": "framework/react/guide/pagination" },
{ "label": "Row Pinning", "to": "framework/react/guide/row-pinning" },
{ "label": "Row Selection", "to": "framework/react/guide/row-selection" },
{ "label": "Sorting", "to": "framework/react/guide/sorting" },
{ "label": "Virtualization", "to": "framework/react/guide/virtualization" }
]
},
{
"label": "preact",
"children": [
{ "label": "Cell Selection", "to": "framework/preact/guide/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/preact/guide/cell-spanning" },
{ "label": "Column Ordering", "to": "framework/preact/guide/column-ordering" },
{ "label": "Column Pinning", "to": "framework/preact/guide/column-pinning" },
{ "label": "Column Sizing", "to": "framework/preact/guide/column-sizing" },
{ "label": "Column Resizing", "to": "framework/preact/guide/column-resizing" },
{ "label": "Column Visibility", "to": "framework/preact/guide/column-visibility" },
{ "label": "Column Filtering", "to": "framework/preact/guide/column-filtering" },
{ "label": "Global Filtering", "to": "framework/preact/guide/global-filtering" },
{ "label": "Fuzzy Filtering", "to": "framework/preact/guide/fuzzy-filtering" },
{ "label": "Faceting", "to": "framework/preact/guide/column-faceting" },
{ "label": "Aggregation", "to": "framework/preact/guide/aggregation" },
{ "label": "Grouping", "to": "framework/preact/guide/grouping" },
{ "label": "Expanding", "to": "framework/preact/guide/expanding" },
{ "label": "Pagination", "to": "framework/preact/guide/pagination" },
{ "label": "Row Pinning", "to": "framework/preact/guide/row-pinning" },
{ "label": "Row Selection", "to": "framework/preact/guide/row-selection" },
{ "label": "Sorting", "to": "framework/preact/guide/sorting" },
{ "label": "Virtualization", "to": "framework/preact/guide/virtualization" }
]
},
{
"label": "octane",
"children": [
{ "label": "Cell Selection", "to": "framework/octane/guide/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/octane/guide/cell-spanning" },
{ "label": "Column Ordering", "to": "framework/octane/guide/column-ordering" },
{ "label": "Column Pinning", "to": "framework/octane/guide/column-pinning" },
{ "label": "Column Sizing", "to": "framework/octane/guide/column-sizing" },
{ "label": "Column Resizing", "to": "framework/octane/guide/column-resizing" },
{ "label": "Column Visibility", "to": "framework/octane/guide/column-visibility" },
{ "label": "Column Filtering", "to": "framework/octane/guide/column-filtering" },
{ "label": "Global Filtering", "to": "framework/octane/guide/global-filtering" },
{ "label": "Fuzzy Filtering", "to": "framework/octane/guide/fuzzy-filtering" },
{ "label": "Faceting", "to": "framework/octane/guide/column-faceting" },
{ "label": "Aggregation", "to": "framework/octane/guide/aggregation" },
{ "label": "Grouping", "to": "framework/octane/guide/grouping" },
{ "label": "Expanding", "to": "framework/octane/guide/expanding" },
{ "label": "Pagination", "to": "framework/octane/guide/pagination" },
{ "label": "Row Pinning", "to": "framework/octane/guide/row-pinning" },
{ "label": "Row Selection", "to": "framework/octane/guide/row-selection" },
{ "label": "Sorting", "to": "framework/octane/guide/sorting" }
]
},
{
"label": "solid",
"children": [
{ "label": "Cell Selection", "to": "framework/solid/guide/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/solid/guide/cell-spanning" },
{ "label": "Column Ordering", "to": "framework/solid/guide/column-ordering" },
{ "label": "Column Pinning", "to": "framework/solid/guide/column-pinning" },
{ "label": "Column Sizing", "to": "framework/solid/guide/column-sizing" },
{ "label": "Column Resizing", "to": "framework/solid/guide/column-resizing" },
{ "label": "Column Visibility", "to": "framework/solid/guide/column-visibility" },
{ "label": "Column Filtering", "to": "framework/solid/guide/column-filtering" },
{ "label": "Global Filtering", "to": "framework/solid/guide/global-filtering" },
{ "label": "Fuzzy Filtering", "to": "framework/solid/guide/fuzzy-filtering" },
{ "label": "Faceting", "to": "framework/solid/guide/column-faceting" },
{ "label": "Aggregation", "to": "framework/solid/guide/aggregation" },
{ "label": "Grouping", "to": "framework/solid/guide/grouping" },
{ "label": "Expanding", "to": "framework/solid/guide/expanding" },
{ "label": "Pagination", "to": "framework/solid/guide/pagination" },
{ "label": "Row Pinning", "to": "framework/solid/guide/row-pinning" },
{ "label": "Row Selection", "to": "framework/solid/guide/row-selection" },
{ "label": "Sorting", "to": "framework/solid/guide/sorting" },
{ "label": "Virtualization", "to": "framework/solid/guide/virtualization" }
]
},
{
"label": "svelte",
"children": [
{ "label": "Cell Selection", "to": "framework/svelte/guide/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/svelte/guide/cell-spanning" },
{ "label": "Column Ordering", "to": "framework/svelte/guide/column-ordering" },
{ "label": "Column Pinning", "to": "framework/svelte/guide/column-pinning" },
{ "label": "Column Sizing", "to": "framework/svelte/guide/column-sizing" },
{ "label": "Column Resizing", "to": "framework/svelte/guide/column-resizing" },
{ "label": "Column Visibility", "to": "framework/svelte/guide/column-visibility" },
{ "label": "Column Filtering", "to": "framework/svelte/guide/column-filtering" },
{ "label": "Global Filtering", "to": "framework/svelte/guide/global-filtering" },
{ "label": "Fuzzy Filtering", "to": "framework/svelte/guide/fuzzy-filtering" },
{ "label": "Faceting", "to": "framework/svelte/guide/column-faceting" },
{ "label": "Aggregation", "to": "framework/svelte/guide/aggregation" },
{ "label": "Grouping", "to": "framework/svelte/guide/grouping" },
{ "label": "Expanding", "to": "framework/svelte/guide/expanding" },
{ "label": "Pagination", "to": "framework/svelte/guide/pagination" },
{ "label": "Row Pinning", "to": "framework/svelte/guide/row-pinning" },
{ "label": "Row Selection", "to": "framework/svelte/guide/row-selection" },
{ "label": "Sorting", "to": "framework/svelte/guide/sorting" },
{ "label": "Virtualization", "to": "framework/svelte/guide/virtualization" }
]
},
{
"label": "vue",
"children": [
{ "label": "Cell Selection", "to": "framework/vue/guide/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/vue/guide/cell-spanning" },
{ "label": "Column Ordering", "to": "framework/vue/guide/column-ordering" },
{ "label": "Column Pinning", "to": "framework/vue/guide/column-pinning" },
{ "label": "Column Sizing", "to": "framework/vue/guide/column-sizing" },
{ "label": "Column Resizing", "to": "framework/vue/guide/column-resizing" },
{ "label": "Column Visibility", "to": "framework/vue/guide/column-visibility" },
{ "label": "Column Filtering", "to": "framework/vue/guide/column-filtering" },
{ "label": "Global Filtering", "to": "framework/vue/guide/global-filtering" },
{ "label": "Fuzzy Filtering", "to": "framework/vue/guide/fuzzy-filtering" },
{ "label": "Faceting", "to": "framework/vue/guide/column-faceting" },
{ "label": "Aggregation", "to": "framework/vue/guide/aggregation" },
{ "label": "Grouping", "to": "framework/vue/guide/grouping" },
{ "label": "Expanding", "to": "framework/vue/guide/expanding" },
{ "label": "Pagination", "to": "framework/vue/guide/pagination" },
{ "label": "Row Pinning", "to": "framework/vue/guide/row-pinning" },
{ "label": "Row Selection", "to": "framework/vue/guide/row-selection" },
{ "label": "Sorting", "to": "framework/vue/guide/sorting" },
{ "label": "Virtualization", "to": "framework/vue/guide/virtualization" }
]
},
{
"label": "vanilla",
"children": [
{ "label": "Aggregation", "to": "framework/vanilla/guide/aggregation" }
]
}
]
},
{
"label": "API Reference",
"children": [
{ "label": "Core API Reference", "to": "reference/index" }
],
"frameworks": [
{
"label": "alpine",
"children": [
{ "label": "Alpine API Reference", "to": "framework/alpine/reference/index" }
]
},
{
"label": "angular",
"children": [
{ "label": "Angular API Reference", "to": "framework/angular/reference/index" }
]
},
{
"label": "ember",
"children": [
{ "label": "Ember API Reference", "to": "framework/ember/reference/index" }
]
},
{
"label": "react",
"children": [
{ "label": "React API Reference", "to": "framework/react/reference/index" }
]
},
{
"label": "preact",
"children": [
{ "label": "Preact API Reference", "to": "framework/preact/reference/index" }
]
},
{
"label": "octane",
"children": [
{ "label": "Octane API Reference", "to": "framework/octane/reference/index" }
]
},
{
"label": "solid",
"children": [
{ "label": "Solid API Reference", "to": "framework/solid/reference/index" }
]
},
{
"label": "svelte",
"children": [
{ "label": "Svelte API Reference", "to": "framework/svelte/reference/index" }
]
},
{
"label": "vue",
"children": [
{ "label": "Vue API Reference", "to": "framework/vue/reference/index" }
]
},
{
"label": "lit",
"children": [
{ "label": "Lit API Reference", "to": "framework/lit/reference/index" }
]
}
]
},
{
"collapsible": true,
"defaultCollapsed": true,
"label": "Table API Reference",
"children": [
{ "label": "Table", "to": "reference/index/type-aliases/Table" },
{ "label": "TableOptions", "to": "reference/index/type-aliases/TableOptions" },
{ "label": "TableState", "to": "reference/index/type-aliases/TableState" },
{ "label": "TableMeta", "to": "reference/index/interfaces/TableMeta" },
{ "label": "TableFeature", "to": "reference/index/interfaces/TableFeature" },
{ "label": "TableFeatures", "to": "reference/index/interfaces/TableFeatures" },
{ "label": "StockFeatures", "to": "reference/index/interfaces/StockFeatures" },
{ "label": "CoreFeatures", "to": "reference/index/interfaces/CoreFeatures" },
{ "label": "BaseAtoms", "to": "reference/index/type-aliases/BaseAtoms" },
{ "label": "Atoms", "to": "reference/index/type-aliases/Atoms" },
{ "label": "ExternalAtoms", "to": "reference/index/type-aliases/ExternalAtoms" },
{ "label": "constructTable", "to": "reference/index/functions/constructTable" },
{ "label": "tableOptions", "to": "reference/index/functions/tableOptions" },
{ "label": "tableFeatures", "to": "reference/index/functions/tableFeatures" },
{ "label": "getInitialTableState", "to": "reference/index/functions/getInitialTableState" },
{ "label": "OnChangeFn", "to": "reference/index/type-aliases/OnChangeFn" },
{ "label": "Updater", "to": "reference/index/type-aliases/Updater" },
{ "label": "DebugOptions", "to": "reference/index/type-aliases/DebugOptions" }
],
"frameworks": [
{
"label": "alpine",
"children": [
{ "label": "createTable", "to": "framework/alpine/reference/functions/createTable" },
{ "label": "createTableHook", "to": "framework/alpine/reference/functions/createTableHook" },
{ "label": "AlpineTable", "to": "framework/alpine/reference/type-aliases/AlpineTable" },
{ "label": "AppAlpineTable", "to": "framework/alpine/reference/type-aliases/AppAlpineTable" },
{ "label": "CreateTableHookOptions", "to": "framework/alpine/reference/type-aliases/CreateTableHookOptions" },
{ "label": "FlexRender", "to": "framework/alpine/reference/functions/FlexRender-1" },
{ "label": "flexRender", "to": "framework/alpine/reference/functions/flexRender" }
]
},
{
"label": "react",
"children": [
{ "label": "useTable", "to": "framework/react/reference/index/functions/useTable" },
{ "label": "createTableHook", "to": "framework/react/reference/index/functions/createTableHook" },
{ "label": "ReactTable", "to": "framework/react/reference/index/type-aliases/ReactTable" },
{ "label": "AppReactTable", "to": "framework/react/reference/index/type-aliases/AppReactTable" },
{ "label": "CreateTableHookOptions", "to": "framework/react/reference/index/type-aliases/CreateTableHookOptions" },
{ "label": "Subscribe", "to": "framework/react/reference/index/functions/Subscribe" },
{ "label": "FlexRender", "to": "framework/react/reference/index/functions/FlexRender-1" },
{ "label": "flexRender", "to": "framework/react/reference/index/functions/flexRender" }
]
},
{
"label": "angular",
"children": [
{ "label": "injectTable", "to": "framework/angular/reference/functions/injectTable" },
{ "label": "createTableHook", "to": "framework/angular/reference/functions/createTableHook" },
{ "label": "AngularTable", "to": "framework/angular/reference/type-aliases/AngularTable" },
{ "label": "AppAngularTable", "to": "framework/angular/reference/type-aliases/AppAngularTable" },
{ "label": "CreateTableHookResult", "to": "framework/angular/reference/type-aliases/CreateTableHookResult" },
{ "label": "CreateTableContextOptions", "to": "framework/angular/reference/type-aliases/CreateTableContextOptions" },
{ "label": "flexRenderComponent", "to": "framework/angular/reference/functions/flexRenderComponent" },
{ "label": "FlexRender", "to": "framework/angular/reference/variables/FlexRender" },
{ "label": "TanStackTable", "to": "framework/angular/reference/classes/TanStackTable" },
{ "label": "injectTableContext", "to": "framework/angular/reference/functions/injectTableContext" }
]
},
{
"label": "ember",
"children": [
{ "label": "useTable", "to": "framework/ember/reference/functions/useTable" },
{ "label": "createTableHook", "to": "framework/ember/reference/functions/createTableHook" },
{ "label": "AppEmberTable", "to": "framework/ember/reference/type-aliases/AppEmberTable" },
{ "label": "CreateTableHookOptions", "to": "framework/ember/reference/type-aliases/CreateTableHookOptions" },
{ "label": "FlexRenderCell", "to": "framework/ember/reference/classes/FlexRenderCell" },
{ "label": "FlexRenderHeader", "to": "framework/ember/reference/classes/FlexRenderHeader" },
{ "label": "FlexRenderFooter", "to": "framework/ember/reference/classes/FlexRenderFooter" },
{ "label": "flexRenderComponent", "to": "framework/ember/reference/functions/flexRenderComponent" },
{ "label": "FlexRenderableSignature", "to": "framework/ember/reference/interfaces/FlexRenderableSignature" },
{ "label": "CellRenderableSignature", "to": "framework/ember/reference/interfaces/CellRenderableSignature" },
{ "label": "emberReactivity", "to": "framework/ember/reference/functions/emberReactivity" },
{ "label": "createAtom", "to": "framework/ember/reference/functions/createAtom" },
{ "label": "signal", "to": "framework/ember/reference/functions/signal" },
{ "label": "computed", "to": "framework/ember/reference/functions/computed" }
]
},
{
"label": "preact",
"children": [
{ "label": "useTable", "to": "framework/preact/reference/functions/useTable" },
{ "label": "createTableHook", "to": "framework/preact/reference/functions/createTableHook" },
{ "label": "PreactTable", "to": "framework/preact/reference/type-aliases/PreactTable" },
{ "label": "AppPreactTable", "to": "framework/preact/reference/type-aliases/AppPreactTable" },
{ "label": "CreateTableHookOptions", "to": "framework/preact/reference/type-aliases/CreateTableHookOptions" },
{ "label": "Subscribe", "to": "framework/preact/reference/functions/Subscribe" },
{ "label": "FlexRender", "to": "framework/preact/reference/functions/FlexRender-1" },
{ "label": "flexRender", "to": "framework/preact/reference/functions/flexRender" }
]
},
{
"label": "octane",
"children": [
{ "label": "useTable", "to": "framework/octane/reference/functions/useTable" },
{ "label": "createTableHook", "to": "framework/octane/reference/functions/createTableHook" },
{ "label": "createTableHookContexts", "to": "framework/octane/reference/functions/createTableHookContexts" },
{ "label": "OctaneTable", "to": "framework/octane/reference/type-aliases/OctaneTable" },
{ "label": "AppOctaneTable", "to": "framework/octane/reference/type-aliases/AppOctaneTable" },
{ "label": "CreateTableHookOptions", "to": "framework/octane/reference/type-aliases/CreateTableHookOptions" },
{ "label": "Subscribe", "to": "framework/octane/reference/variables/Subscribe" },
{ "label": "FlexRender", "to": "framework/octane/reference/functions/FlexRender-1" },
{ "label": "flexRender", "to": "framework/octane/reference/functions/flexRender" }
]
},
{
"label": "solid",
"children": [
{ "label": "createTable", "to": "framework/solid/reference/functions/createTable" },
{ "label": "createTableHook", "to": "framework/solid/reference/functions/createTableHook" },
{ "label": "SolidTable", "to": "framework/solid/reference/type-aliases/SolidTable" },
{ "label": "AppSolidTable", "to": "framework/solid/reference/type-aliases/AppSolidTable" },
{ "label": "CreateTableHookOptions", "to": "framework/solid/reference/type-aliases/CreateTableHookOptions" },
{ "label": "FlexRender", "to": "framework/solid/reference/functions/FlexRender-1" },
{ "label": "flexRender", "to": "framework/solid/reference/functions/flexRender" }
]
},
{
"label": "svelte",
"children": [
{ "label": "createTable", "to": "framework/svelte/reference/functions/createTable" },
{ "label": "createTableHook", "to": "framework/svelte/reference/functions/createTableHook" },
{ "label": "createTableState", "to": "framework/svelte/reference/functions/createTableState" },
{ "label": "SvelteTable", "to": "framework/svelte/reference/type-aliases/SvelteTable" },
{ "label": "AppSvelteTable", "to": "framework/svelte/reference/type-aliases/AppSvelteTable" },
{ "label": "renderComponent", "to": "framework/svelte/reference/functions/renderComponent" },
{ "label": "renderSnippet", "to": "framework/svelte/reference/functions/renderSnippet" },
{ "label": "FlexRender", "to": "framework/svelte/reference/variables/FlexRender" }
]
},
{
"label": "vue",
"children": [
{ "label": "useTable", "to": "framework/vue/reference/functions/useTable" },
{ "label": "createTableHook", "to": "framework/vue/reference/functions/createTableHook" },
{ "label": "VueTable", "to": "framework/vue/reference/type-aliases/VueTable" },
{ "label": "AppVueTable", "to": "framework/vue/reference/type-aliases/AppVueTable" },
{ "label": "CreateTableHookOptions", "to": "framework/vue/reference/type-aliases/CreateTableHookOptions" },
{ "label": "FlexRender", "to": "framework/vue/reference/variables/FlexRender" },
{ "label": "flexRender", "to": "framework/vue/reference/functions/flexRender" }
]
},
{
"label": "lit",
"children": [
{ "label": "TableController", "to": "framework/lit/reference/classes/TableController" },
{ "label": "createTableHook", "to": "framework/lit/reference/functions/createTableHook" },
{ "label": "LitTable", "to": "framework/lit/reference/type-aliases/LitTable" },
{ "label": "AppLitTable", "to": "framework/lit/reference/type-aliases/AppLitTable" },
{ "label": "CreateTableHookOptions", "to": "framework/lit/reference/type-aliases/CreateTableHookOptions" },
{ "label": "subscribe", "to": "framework/lit/reference/variables/subscribe" },
{ "label": "SubscribeDirective", "to": "framework/lit/reference/classes/SubscribeDirective" },
{ "label": "SelectionSource", "to": "framework/lit/reference/type-aliases/SelectionSource" },
{ "label": "FlexRender", "to": "framework/lit/reference/functions/FlexRender-1" },
{ "label": "flexRender", "to": "framework/lit/reference/functions/flexRender" }
]
}
]
},
{
"collapsible": true,
"defaultCollapsed": true,
"label": "Column API Reference",
"children": [
{ "label": "Column", "to": "reference/index/type-aliases/Column" },
{ "label": "ColumnDef", "to": "reference/index/type-aliases/ColumnDef" },
{ "label": "ColumnDefBase", "to": "reference/index/type-aliases/ColumnDefBase" },
{ "label": "AccessorColumnDef", "to": "reference/index/type-aliases/AccessorColumnDef" },
{ "label": "DisplayColumnDef", "to": "reference/index/type-aliases/DisplayColumnDef" },
{ "label": "GroupColumnDef", "to": "reference/index/type-aliases/GroupColumnDef" },
{ "label": "ColumnDefTemplate", "to": "reference/index/type-aliases/ColumnDefTemplate" },
{ "label": "ColumnHelper", "to": "reference/index/interfaces/ColumnHelper" },
{ "label": "ColumnMeta", "to": "reference/index/interfaces/ColumnMeta" },
{ "label": "createColumnHelper", "to": "reference/index/functions/createColumnHelper" },
{ "label": "constructColumn", "to": "reference/index/functions/constructColumn" },
{ "label": "AccessorFn", "to": "reference/index/type-aliases/AccessorFn" },
{ "label": "DeepKeys", "to": "reference/index/type-aliases/DeepKeys" },
{ "label": "DeepValue", "to": "reference/index/type-aliases/DeepValue" }
],
"frameworks": [
{
"label": "alpine",
"children": [
{ "label": "AppColumnHelper", "to": "framework/alpine/reference/type-aliases/AppColumnHelper" }
]
},
{
"label": "react",
"children": [
{ "label": "AppColumnHelper", "to": "framework/react/reference/index/type-aliases/AppColumnHelper" }
]
},
{
"label": "angular",
"children": [
{ "label": "AppColumnHelper", "to": "framework/angular/reference/type-aliases/AppColumnHelper" }
]
},
{
"label": "ember",
"children": [
{ "label": "AppColumnHelper", "to": "framework/ember/reference/type-aliases/AppColumnHelper" }
]
},
{
"label": "preact",
"children": [
{ "label": "AppColumnHelper", "to": "framework/preact/reference/type-aliases/AppColumnHelper" }
]
},
{
"label": "octane",
"children": [
{ "label": "AppColumnHelper", "to": "framework/octane/reference/interfaces/AppColumnHelper" }
]
},
{
"label": "solid",
"children": [
{ "label": "AppColumnHelper", "to": "framework/solid/reference/type-aliases/AppColumnHelper" }
]
},
{
"label": "svelte",
"children": [
{ "label": "AppColumnHelper", "to": "framework/svelte/reference/type-aliases/AppColumnHelper" }
]
},
{
"label": "vue",
"children": [
{ "label": "AppColumnHelper", "to": "framework/vue/reference/type-aliases/AppColumnHelper" }
]
},
{
"label": "lit",
"children": [
{ "label": "AppColumnHelper", "to": "framework/lit/reference/type-aliases/AppColumnHelper" }
]
}
]
},
{
"collapsible": true,
"defaultCollapsed": true,
"label": "Row API Reference",
"children": [
{ "label": "Row", "to": "reference/index/type-aliases/Row" },
{ "label": "RowData", "to": "reference/index/type-aliases/RowData" },
{ "label": "RowModel", "to": "reference/index/interfaces/RowModel" },
{ "label": "constructRow", "to": "reference/index/functions/constructRow" },
{ "label": "createCoreRowModel", "to": "reference/index/functions/createCoreRowModel" },
{ "label": "createFilteredRowModel", "to": "reference/index/functions/createFilteredRowModel" },
{ "label": "createSortedRowModel", "to": "reference/index/functions/createSortedRowModel" },
{ "label": "createGroupedRowModel", "to": "reference/index/functions/createGroupedRowModel" },
{ "label": "createExpandedRowModel", "to": "reference/index/functions/createExpandedRowModel" },
{ "label": "createPaginatedRowModel", "to": "reference/index/functions/createPaginatedRowModel" },
{ "label": "createFacetedRowModel", "to": "reference/index/functions/createFacetedRowModel" },
{ "label": "createFacetedMinMaxValues", "to": "reference/index/functions/createFacetedMinMaxValues" },
{ "label": "createFacetedUniqueValues", "to": "reference/index/functions/createFacetedUniqueValues" },
{ "label": "expandRows", "to": "reference/index/functions/expandRows" },
{ "label": "RowModelFns", "to": "reference/index/type-aliases/RowModelFns" },
{ "label": "CachedRowModels", "to": "reference/index/type-aliases/CachedRowModels" }
]
},
{
"collapsible": true,
"defaultCollapsed": true,
"label": "Cell API Reference",
"children": [
{ "label": "Cell", "to": "reference/index/type-aliases/Cell" },
{ "label": "CellContext", "to": "reference/index/interfaces/CellContext" },
{ "label": "CellData", "to": "reference/index/type-aliases/CellData" },
{ "label": "constructCell", "to": "reference/index/functions/constructCell" }
],
"frameworks": [
{
"label": "react",
"children": [
{ "label": "AppCellContext", "to": "framework/react/reference/index/type-aliases/AppCellContext" }
]
},
{
"label": "angular",
"children": [
{ "label": "AppCellContext", "to": "framework/angular/reference/type-aliases/AppCellContext" }
]
},
{
"label": "preact",
"children": [
{ "label": "AppCellContext", "to": "framework/preact/reference/type-aliases/AppCellContext" }
]
},
{
"label": "octane",
"children": [
{ "label": "AppCellContext", "to": "framework/octane/reference/interfaces/AppCellContext" }
]
},
{
"label": "solid",
"children": [
{ "label": "AppCellContext", "to": "framework/solid/reference/type-aliases/AppCellContext" }
]
},
{
"label": "svelte",
"children": [
{ "label": "AppCellContext", "to": "framework/svelte/reference/type-aliases/AppCellContext" }
]
},
{
"label": "vue",
"children": [
{ "label": "AppCellContext", "to": "framework/vue/reference/type-aliases/AppCellContext" }
]
},
{
"label": "lit",
"children": [
{ "label": "AppCellContext", "to": "framework/lit/reference/type-aliases/AppCellContext" }
]
}
]
},
{
"collapsible": true,
"defaultCollapsed": true,
"label": "Header API Reference",
"children": [
{ "label": "Header", "to": "reference/index/type-aliases/Header" },
{ "label": "HeaderGroup", "to": "reference/index/interfaces/HeaderGroup" },
{ "label": "HeaderContext", "to": "reference/index/interfaces/HeaderContext" },
{ "label": "constructHeader", "to": "reference/index/functions/constructHeader" },
{ "label": "buildHeaderGroups", "to": "reference/index/functions/buildHeaderGroups" }
],
"frameworks": [
{
"label": "react",
"children": [
{ "label": "AppHeaderContext", "to": "framework/react/reference/index/type-aliases/AppHeaderContext" }
]
},
{
"label": "angular",
"children": [
{ "label": "AppHeaderContext", "to": "framework/angular/reference/type-aliases/AppHeaderContext" }
]
},
{
"label": "preact",
"children": [
{ "label": "AppHeaderContext", "to": "framework/preact/reference/type-aliases/AppHeaderContext" }
]
},
{
"label": "octane",
"children": [
{ "label": "AppHeaderContext", "to": "framework/octane/reference/interfaces/AppHeaderContext" }
]
},
{
"label": "solid",
"children": [
{ "label": "AppHeaderContext", "to": "framework/solid/reference/type-aliases/AppHeaderContext" }
]
},
{
"label": "svelte",
"children": [
{ "label": "AppHeaderContext", "to": "framework/svelte/reference/type-aliases/AppHeaderContext" }
]
},
{
"label": "vue",
"children": [
{ "label": "AppHeaderContext", "to": "framework/vue/reference/type-aliases/AppHeaderContext" }
]
},
{
"label": "lit",
"children": [
{ "label": "AppHeaderContext", "to": "framework/lit/reference/type-aliases/AppHeaderContext" }
]
}
]
},
{
"collapsible": true,
"defaultCollapsed": true,
"label": "Features API Reference",
"children": [
{ "label": "stockFeatures", "to": "reference/index/variables/stockFeatures" },
{ "label": "coreFeatures", "to": "reference/index/variables/coreFeatures" },
{ "label": "coreCellsFeature", "to": "reference/index/variables/coreCellsFeature" },
{ "label": "coreColumnsFeature", "to": "reference/index/variables/coreColumnsFeature" },
{ "label": "coreHeadersFeature", "to": "reference/index/variables/coreHeadersFeature" },
{ "label": "coreRowModelsFeature", "to": "reference/index/variables/coreRowModelsFeature" },
{ "label": "coreRowsFeature", "to": "reference/index/variables/coreRowsFeature" },
{ "label": "coreTablesFeature", "to": "reference/index/variables/coreTablesFeature" },
{ "label": "cellSpanningFeature", "to": "reference/index/variables/cellSpanningFeature" },
{ "label": "columnFilteringFeature", "to": "reference/index/variables/columnFilteringFeature" },
{ "label": "columnFacetingFeature", "to": "reference/index/variables/columnFacetingFeature" },
{ "label": "rowAggregationFeature", "to": "reference/index/variables/rowAggregationFeature" },
{ "label": "columnGroupingFeature", "to": "reference/index/variables/columnGroupingFeature" },
{ "label": "columnOrderingFeature", "to": "reference/index/variables/columnOrderingFeature" },
{ "label": "columnPinningFeature", "to": "reference/index/variables/columnPinningFeature" },
{ "label": "columnResizingFeature", "to": "reference/index/variables/columnResizingFeature" },
{ "label": "columnSizingFeature", "to": "reference/index/variables/columnSizingFeature" },
{ "label": "columnVisibilityFeature", "to": "reference/index/variables/columnVisibilityFeature" },
{ "label": "globalFilteringFeature", "to": "reference/index/variables/globalFilteringFeature" },
{ "label": "rowExpandingFeature", "to": "reference/index/variables/rowExpandingFeature" },
{ "label": "rowPaginationFeature", "to": "reference/index/variables/rowPaginationFeature" },
{ "label": "rowPinningFeature", "to": "reference/index/variables/rowPinningFeature" },
{ "label": "rowSelectionFeature", "to": "reference/index/variables/rowSelectionFeature" },
{ "label": "rowSortingFeature", "to": "reference/index/variables/rowSortingFeature" },
{ "label": "filterFns", "to": "reference/index/variables/filterFns" },
{ "label": "sortFns", "to": "reference/index/variables/sortFns" },
{ "label": "aggregationFns", "to": "reference/index/variables/aggregationFns" },
{ "label": "ColumnFilter", "to": "reference/index/interfaces/ColumnFilter" },
{ "label": "ColumnPinningState", "to": "reference/index/interfaces/ColumnPinningState" },
{ "label": "ColumnSort", "to": "reference/index/interfaces/ColumnSort" },
{ "label": "PaginationState", "to": "reference/index/interfaces/PaginationState" },
{ "label": "RowPinningState", "to": "reference/index/interfaces/RowPinningState" },
{ "label": "columnResizingState", "to": "reference/index/interfaces/columnResizingState" },
{ "label": "FilterFn", "to": "reference/index/interfaces/FilterFn" },
{ "label": "FilterFns", "to": "reference/index/interfaces/FilterFns" },
{ "label": "FilterMeta", "to": "reference/index/interfaces/FilterMeta" },
{ "label": "SortFn", "to": "reference/index/interfaces/SortFn" },
{ "label": "SortFns", "to": "reference/index/interfaces/SortFns" },
{ "label": "AggregationFns", "to": "reference/index/interfaces/AggregationFns" },
{ "label": "AggregationContext", "to": "reference/index/interfaces/AggregationContext" },
{ "label": "AggregationMergeContext", "to": "reference/index/interfaces/AggregationMergeContext" },
{ "label": "AggregationFnDef", "to": "reference/index/interfaces/AggregationFnDef" },
{ "label": "AggregationFnDescriptor", "to": "reference/index/interfaces/AggregationFnDescriptor" },
{ "label": "AggregationValueContext", "to": "reference/index/interfaces/AggregationValueContext" },
{ "label": "AggregationValueResult", "to": "reference/index/interfaces/AggregationValueResult" },
{ "label": "constructAggregationFn", "to": "reference/index/functions/constructAggregationFn" },
{ "label": "ColumnFiltersState", "to": "reference/index/type-aliases/ColumnFiltersState" },
{ "label": "ColumnOrderState", "to": "reference/index/type-aliases/ColumnOrderState" },
{ "label": "ColumnPinningPosition", "to": "reference/index/type-aliases/ColumnPinningPosition" },
{ "label": "ColumnResizeDirection", "to": "reference/index/type-aliases/ColumnResizeDirection" },
{ "label": "ColumnResizeMode", "to": "reference/index/type-aliases/ColumnResizeMode" },
{ "label": "ColumnSizingState", "to": "reference/index/type-aliases/ColumnSizingState" },
{ "label": "ColumnVisibilityState", "to": "reference/index/type-aliases/ColumnVisibilityState" },
{ "label": "ExpandedState", "to": "reference/index/type-aliases/ExpandedState" },
{ "label": "GroupingState", "to": "reference/index/type-aliases/GroupingState" },
{ "label": "RowPinningPosition", "to": "reference/index/type-aliases/RowPinningPosition" },
{ "label": "RowSelectionState", "to": "reference/index/type-aliases/RowSelectionState" },
{ "label": "SortDirection", "to": "reference/index/type-aliases/SortDirection" },
{ "label": "SortingState", "to": "reference/index/type-aliases/SortingState" },
{ "label": "AggregationFnOption", "to": "reference/index/type-aliases/AggregationFnOption" },
{ "label": "AggregationResult", "to": "reference/index/type-aliases/AggregationResult" },
{ "label": "FilterFnOption", "to": "reference/index/type-aliases/FilterFnOption" },
{ "label": "SortFnOption", "to": "reference/index/type-aliases/SortFnOption" },
{ "label": "BuiltInAggregationFn", "to": "reference/index/type-aliases/BuiltInAggregationFn" },
{ "label": "BuiltInFilterFn", "to": "reference/index/type-aliases/BuiltInFilterFn" },
{ "label": "BuiltInSortFn", "to": "reference/index/type-aliases/BuiltInSortFn" }
]
},
{
"collapsible": true,
"defaultCollapsed": true,
"label": "Static Functions API Reference",
"children": [
{ "label": "Static Functions Overview", "to": "reference/static-functions/index" }
]
},
{
"collapsible": true,
"defaultCollapsed": true,
"label": "Legacy API Reference",
"children": [],
"frameworks": [
{
"label": "react",
"children": [
{ "label": "Legacy API Overview", "to": "framework/react/reference/legacy/index" },
{ "label": "useLegacyTable", "to": "framework/react/reference/legacy/functions/useLegacyTable" },
{ "label": "legacyCreateColumnHelper", "to": "framework/react/reference/legacy/functions/legacyCreateColumnHelper" },
{ "label": "getCoreRowModel", "to": "framework/react/reference/legacy/functions/getCoreRowModel" },
{ "label": "getFilteredRowModel", "to": "framework/react/reference/legacy/functions/getFilteredRowModel" },
{ "label": "getSortedRowModel", "to": "framework/react/reference/legacy/functions/getSortedRowModel" },
{ "label": "getGroupedRowModel", "to": "framework/react/reference/legacy/functions/getGroupedRowModel" },
{ "label": "getExpandedRowModel", "to": "framework/react/reference/legacy/functions/getExpandedRowModel" },
{ "label": "getPaginationRowModel", "to": "framework/react/reference/legacy/functions/getPaginationRowModel" },
{ "label": "getFacetedRowModel", "to": "framework/react/reference/legacy/functions/getFacetedRowModel" },
{ "label": "getFacetedMinMaxValues", "to": "framework/react/reference/legacy/functions/getFacetedMinMaxValues" },
{ "label": "getFacetedUniqueValues", "to": "framework/react/reference/legacy/functions/getFacetedUniqueValues" },
{ "label": "LegacyRowModelOptions", "to": "framework/react/reference/legacy/interfaces/LegacyRowModelOptions" },
{ "label": "LegacyTable", "to": "framework/react/reference/legacy/type-aliases/LegacyTable" },
{ "label": "LegacyTableOptions", "to": "framework/react/reference/legacy/type-aliases/LegacyTableOptions" },
{ "label": "LegacyReactTable", "to": "framework/react/reference/legacy/type-aliases/LegacyReactTable" },
{ "label": "LegacyColumnDef", "to": "framework/react/reference/legacy/type-aliases/LegacyColumnDef" }
]
}
]
},
{
"label": "Basic Examples",
"children": [],
"frameworks": [
{
"label": "alpine",
"children": [
{ "to": "framework/alpine/examples/basic-create-table", "label": "Basic (createTable)" },
{ "to": "framework/alpine/examples/basic-app-table", "label": "Basic (createAppTable)" },
{ "to": "framework/alpine/examples/basic-external-state", "label": "Basic (External State)" },
{ "to": "framework/alpine/examples/basic-external-atoms", "label": "Basic (External Atoms)" },
{ "to": "framework/alpine/examples/basic-dynamic-columns", "label": "Basic (Dynamic Columns)" },
{ "to": "framework/alpine/examples/header-groups", "label": "Header Groups" }
]
},
{
"label": "angular",
"children": [
{ "label": "Basic (injectTable)", "to": "framework/angular/examples/basic-inject-table" },
{ "label": "Basic (createAppTable)", "to": "framework/angular/examples/basic-app-table" },
{ "label": "Basic (External State)", "to": "framework/angular/examples/basic-external-state" },
{ "label": "Basic (External Atoms)", "to": "framework/angular/examples/basic-external-atoms" },
{ "label": "Basic (Dynamic Columns)", "to": "framework/angular/examples/basic-dynamic-columns" },
{ "label": "Header Groups", "to": "framework/angular/examples/header-groups" }
]
},
{
"label": "ember",
"children": [
{ "label": "Basic (useTable)", "to": "framework/ember/examples/basic-table" },
{ "label": "Basic (createAppTable)", "to": "framework/ember/examples/basic-app-table" },
{ "label": "Basic (External State)", "to": "framework/ember/examples/basic-external-state" },
{ "label": "Basic (External Atoms)", "to": "framework/ember/examples/basic-external-atoms" },
{ "label": "Header Groups", "to": "framework/ember/examples/header-groups" }
]
},
{
"label": "lit",
"children": [
{ "label": "Basic (TableController)", "to": "framework/lit/examples/basic-table-controller" },
{ "label": "Basic (useAppTable)", "to": "framework/lit/examples/basic-app-table" },
{ "label": "Basic (External State)", "to": "framework/lit/examples/basic-external-state" },
{ "label": "Basic (External Atoms)", "to": "framework/lit/examples/basic-external-atoms" },
{ "label": "Basic (Subscribe)", "to": "framework/lit/examples/basic-subscribe" },
{ "label": "Basic (Dynamic Columns)", "to": "framework/lit/examples/basic-dynamic-columns" },
{ "label": "Header Groups", "to": "framework/lit/examples/header-groups" }
]
},
{
"label": "react",
"children": [
{ "label": "Basic (useTable)", "to": "framework/react/examples/basic-use-table" },
{ "label": "Basic (useAppTable)", "to": "framework/react/examples/basic-use-app-table" },
{ "label": "Basic (useLegacyTable)", "to": "framework/react/examples/basic-use-legacy-table" },
{ "label": "Basic (External State)", "to": "framework/react/examples/basic-external-state" },
{ "label": "Basic (External Atoms)", "to": "framework/react/examples/basic-external-atoms" },
{ "label": "Basic (Subscribe)", "to": "framework/react/examples/basic-subscribe" },
{ "label": "Basic (Dynamic Columns)", "to": "framework/react/examples/basic-dynamic-columns" },
{ "label": "Header Groups", "to": "framework/react/examples/header-groups" }
]
},
{
"label": "solid",
"children": [
{ "label": "Basic (createTable)", "to": "framework/solid/examples/basic-use-table" },
{ "label": "Basic (createAppTable)", "to": "framework/solid/examples/basic-app-table" },
{ "label": "Basic (External State)", "to": "framework/solid/examples/basic-external-state" },
{ "label": "Basic (External Atoms)", "to": "framework/solid/examples/basic-external-atoms" },
{ "label": "Basic (Dynamic Columns)", "to": "framework/solid/examples/basic-dynamic-columns" },
{ "label": "Header Groups", "to": "framework/solid/examples/header-groups" }
]
},
{
"label": "svelte",
"children": [
{ "label": "Basic (createTable)", "to": "framework/svelte/examples/basic-create-table" },
{ "label": "Basic (createAppTable)", "to": "framework/svelte/examples/basic-app-table" },
{ "label": "Basic (Snippets)", "to": "framework/svelte/examples/basic-snippets" },
{ "label": "Basic (External State)", "to": "framework/svelte/examples/basic-external-state" },
{ "label": "Basic (External Atoms)", "to": "framework/svelte/examples/basic-external-atoms" },
{ "label": "Basic (Dynamic Columns)", "to": "framework/svelte/examples/basic-dynamic-columns" },
{ "label": "Header Groups", "to": "framework/svelte/examples/header-groups" }
]
},
{
"label": "vue",
"children": [
{ "label": "Basic (useTable)", "to": "framework/vue/examples/basic-use-table" },
{ "label": "Basic (useAppTable)", "to": "framework/vue/examples/basic-use-app-table" },
{ "label": "Basic (External State)", "to": "framework/vue/examples/basic-external-state" },
{ "label": "Basic (External Atoms)", "to": "framework/vue/examples/basic-external-atoms" },
{ "label": "Basic (Dynamic Columns)", "to": "framework/vue/examples/basic-dynamic-columns" },
{ "label": "Header Groups", "to": "framework/vue/examples/header-groups" }
]
},
{
"label": "preact",
"children": [
{ "label": "Basic (useTable)", "to": "framework/preact/examples/basic-use-table" },
{ "label": "Basic (useAppTable)", "to": "framework/preact/examples/basic-use-app-table" },
{ "label": "Basic (External State)", "to": "framework/preact/examples/basic-external-state" },
{ "label": "Basic (External Atoms)", "to": "framework/preact/examples/basic-external-atoms" },
{ "label": "Basic (Subscribe)", "to": "framework/preact/examples/basic-subscribe" },
{ "label": "Basic (Dynamic Columns)", "to": "framework/preact/examples/basic-dynamic-columns" },
{ "label": "Header Groups", "to": "framework/preact/examples/header-groups" }
]
},
{
"label": "octane",
"children": [
{ "label": "Basic (useTable)", "to": "framework/octane/examples/basic-use-table" },
{ "label": "Basic (useAppTable)", "to": "framework/octane/examples/basic-use-app-table" },
{ "label": "Basic (External State)", "to": "framework/octane/examples/basic-external-state" },
{ "label": "Basic (External Atoms)", "to": "framework/octane/examples/basic-external-atoms" },
{ "label": "Basic (Subscribe)", "to": "framework/octane/examples/basic-subscribe" },
{ "label": "Basic (Dynamic Columns)", "to": "framework/octane/examples/basic-dynamic-columns" },
{ "label": "Header Groups", "to": "framework/octane/examples/header-groups" }
]
},
{
"label": "vanilla",
"children": [
{ "label": "Basic", "to": "framework/vanilla/examples/basic" }
]
}
]
},
{
"label": "Feature Examples",
"children": [],
"frameworks": [
{
"label": "alpine",
"children": [
{ "to": "framework/alpine/examples/cell-selection", "label": "Cell Selection" },
{ "to": "framework/alpine/examples/cell-spanning", "label": "Cell Spanning" },
{ "to": "framework/alpine/examples/filters", "label": "Column Filters" },
{ "to": "framework/alpine/examples/filters-faceted", "label": "Column Filters (Faceted)" },
{ "to": "framework/alpine/examples/filters-faceted-bucketed", "label": "Bucketed Faceted Filters" },
{ "to": "framework/alpine/examples/column-ordering", "label": "Column Ordering" },
{ "to": "framework/alpine/examples/column-pinning", "label": "Column Pinning" },
{ "to": "framework/alpine/examples/column-pinning-split", "label": "Column Pinning (Split)" },
{ "to": "framework/alpine/examples/column-pinning-sticky", "label": "Sticky Column Pinning" },
{ "to": "framework/alpine/examples/column-sizing", "label": "Column Sizing" },
{ "to": "framework/alpine/examples/column-resizing", "label": "Column Resizing" },
{ "to": "framework/alpine/examples/column-resizing-performant", "label": "Performant Column Resizing" },
{ "to": "framework/alpine/examples/column-visibility", "label": "Column Visibility" },
{ "to": "framework/alpine/examples/expanding", "label": "Expanding" },
{ "to": "framework/alpine/examples/sub-components", "label": "Expanding Sub Components" },
{ "to": "framework/alpine/examples/grouping", "label": "Grouping" },
{ "to": "framework/alpine/examples/aggregation", "label": "Aggregation" },
{ "to": "framework/alpine/examples/grouped-aggregation", "label": "Grouped Aggregation" },
{ "to": "framework/alpine/examples/pagination", "label": "Pagination" },
{ "to": "framework/alpine/examples/row-pinning", "label": "Row Pinning" },
{ "to": "framework/alpine/examples/row-selection", "label": "Row Selection" },
{ "to": "framework/alpine/examples/sorting", "label": "Sorting" },
{ "to": "framework/alpine/examples/sorting-dynamic-data", "label": "Sorting (Dynamic Data)" }
]
},
{
"label": "angular",
"children": [
{ "label": "Kitchen Sink (All Features)", "to": "framework/angular/examples/kitchen-sink" },
{ "label": "Cell Selection", "to": "framework/angular/examples/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/angular/examples/cell-spanning" },
{ "label": "Column Filters", "to": "framework/angular/examples/filters" },
{ "label": "Column Filters (Faceted)", "to": "framework/angular/examples/filters-faceted" },
{ "label": "Bucketed Faceted Filters", "to": "framework/angular/examples/filters-faceted-bucketed" },
{ "label": "Fuzzy Search Filters", "to": "framework/angular/examples/filters-fuzzy" },
{ "label": "Column Ordering", "to": "framework/angular/examples/column-ordering" },
{ "label": "Column Pinning", "to": "framework/angular/examples/column-pinning" },
{ "label": "Column Pinning (Split)", "to": "framework/angular/examples/column-pinning-split" },
{ "label": "Sticky Column Pinning", "to": "framework/angular/examples/column-pinning-sticky" },
{ "label": "Column Sizing", "to": "framework/angular/examples/column-sizing" },
{ "label": "Column Resizing", "to": "framework/angular/examples/column-resizing" },
{ "label": "Performant Column Resizing", "to": "framework/angular/examples/column-resizing-performant" },
{ "label": "Column Visibility", "to": "framework/angular/examples/column-visibility" },
{ "label": "Expanding", "to": "framework/angular/examples/expanding" },
{ "label": "Expanding Sub Components", "to": "framework/angular/examples/sub-components" },
{ "label": "Grouping", "to": "framework/angular/examples/grouping" },
{ "label": "Aggregation", "to": "framework/angular/examples/aggregation" },
{ "label": "Grouped Aggregation", "to": "framework/angular/examples/grouped-aggregation" },
{ "label": "Pagination", "to": "framework/angular/examples/pagination" },
{ "label": "Row Pinning", "to": "framework/angular/examples/row-pinning" },
{ "label": "Row Selection", "to": "framework/angular/examples/row-selection" },
{ "label": "Sorting", "to": "framework/angular/examples/sorting" }
]
},
{
"label": "ember",
"children": [
{ "label": "Kitchen Sink (All Features)", "to": "framework/ember/examples/kitchen-sink" },
{ "label": "Cell Selection", "to": "framework/ember/examples/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/ember/examples/cell-spanning" },
{ "label": "Column Filters", "to": "framework/ember/examples/filters" },
{ "label": "Column Filters (Faceted)", "to": "framework/ember/examples/filters-faceted" },
{ "label": "Bucketed Faceted Filters", "to": "framework/ember/examples/filters-faceted-bucketed" },
{ "label": "Fuzzy Search Filters", "to": "framework/ember/examples/filters-fuzzy" },
{ "label": "Column Ordering", "to": "framework/ember/examples/column-ordering" },
{ "label": "Column Pinning", "to": "framework/ember/examples/column-pinning" },
{ "label": "Column Pinning (Split)", "to": "framework/ember/examples/column-pinning-split" },
{ "label": "Sticky Column Pinning", "to": "framework/ember/examples/column-pinning-sticky" },
{ "label": "Column Sizing", "to": "framework/ember/examples/column-sizing" },
{ "label": "Column Resizing", "to": "framework/ember/examples/column-resizing" },
{ "label": "Performant Column Resizing", "to": "framework/ember/examples/column-resizing-performant" },
{ "label": "Column Visibility", "to": "framework/ember/examples/column-visibility" },
{ "label": "Expanding", "to": "framework/ember/examples/expanding" },
{ "label": "Expanding Sub Components", "to": "framework/ember/examples/sub-components" },
{ "label": "Grouping", "to": "framework/ember/examples/grouping" },
{ "label": "Aggregation", "to": "framework/ember/examples/aggregation" },
{ "label": "Grouped Aggregation", "to": "framework/ember/examples/grouped-aggregation" },
{ "label": "Pagination", "to": "framework/ember/examples/pagination" },
{ "label": "Row Pinning", "to": "framework/ember/examples/row-pinning" },
{ "label": "Row Selection", "to": "framework/ember/examples/row-selection" },
{ "label": "Sorting", "to": "framework/ember/examples/sorting" }
]
},
{
"label": "lit",
"children": [
{ "label": "Kitchen Sink (All Features)", "to": "framework/lit/examples/kitchen-sink" },
{ "label": "Cell Selection", "to": "framework/lit/examples/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/lit/examples/cell-spanning" },
{ "label": "Column Filters", "to": "framework/lit/examples/filters" },
{ "label": "Column Filters (Faceted)", "to": "framework/lit/examples/filters-faceted" },
{ "label": "Bucketed Faceted Filters", "to": "framework/lit/examples/filters-faceted-bucketed" },
{ "label": "Fuzzy Search Filters", "to": "framework/lit/examples/filters-fuzzy" },
{ "label": "Column Ordering", "to": "framework/lit/examples/column-ordering" },
{ "label": "Column Pinning", "to": "framework/lit/examples/column-pinning" },
{ "label": "Column Pinning (Split)", "to": "framework/lit/examples/column-pinning-split" },
{ "label": "Sticky Column Pinning", "to": "framework/lit/examples/column-pinning-sticky" },
{ "label": "Column Sizing", "to": "framework/lit/examples/column-sizing" },
{ "label": "Column Resizing", "to": "framework/lit/examples/column-resizing" },
{ "label": "Performant Column Resizing", "to": "framework/lit/examples/column-resizing-performant" },
{ "label": "Column Visibility", "to": "framework/lit/examples/column-visibility" },
{ "label": "Expanding", "to": "framework/lit/examples/expanding" },
{ "label": "Expanding Sub Components", "to": "framework/lit/examples/sub-components" },
{ "label": "Grouping", "to": "framework/lit/examples/grouping" },
{ "label": "Aggregation", "to": "framework/lit/examples/aggregation" },
{ "label": "Grouped Aggregation", "to": "framework/lit/examples/grouped-aggregation" },
{ "label": "Pagination", "to": "framework/lit/examples/pagination" },
{ "label": "Row Pinning", "to": "framework/lit/examples/row-pinning" },
{ "label": "Row Selection", "to": "framework/lit/examples/row-selection" },
{ "label": "Sorting", "to": "framework/lit/examples/sorting" },
{ "label": "Sorting (Dynamic Data)", "to": "framework/lit/examples/sorting-dynamic-data" }
]
},
{
"label": "react",
"children": [
{ "label": "Kitchen Sink (All Features)", "to": "framework/react/examples/kitchen-sink" },
{ "label": "Cell Selection", "to": "framework/react/examples/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/react/examples/cell-spanning" },
{ "label": "Column Filters", "to": "framework/react/examples/filters" },
{ "label": "Column Filters (Faceted)", "to": "framework/react/examples/filters-faceted" },
{ "label": "Bucketed Faceted Filters", "to": "framework/react/examples/filters-faceted-bucketed" },
{ "label": "Fuzzy Search Filters", "to": "framework/react/examples/filters-fuzzy" },
{ "label": "Column Ordering", "to": "framework/react/examples/column-ordering" },
{ "label": "Column Ordering (DnD)", "to": "framework/react/examples/column-dnd" },
{ "label": "Column Pinning", "to": "framework/react/examples/column-pinning" },
{ "label": "Column Pinning (Split)", "to": "framework/react/examples/column-pinning-split" },
{ "label": "Sticky Column Pinning", "to": "framework/react/examples/column-pinning-sticky" },
{ "label": "Column Sizing", "to": "framework/react/examples/column-sizing" },
{ "label": "Column Resizing", "to": "framework/react/examples/column-resizing" },
{ "label": "Performant Column Resizing", "to": "framework/react/examples/column-resizing-performant" },
{ "label": "Column Visibility", "to": "framework/react/examples/column-visibility" },
{ "label": "Expanding", "to": "framework/react/examples/expanding" },
{ "label": "Expanding Sub Components", "to": "framework/react/examples/sub-components" },
{ "label": "Grouping", "to": "framework/react/examples/grouping" },
{ "label": "Aggregation", "to": "framework/react/examples/aggregation" },
{ "label": "Grouped Aggregation", "to": "framework/react/examples/grouped-aggregation" },
{ "label": "Pagination", "to": "framework/react/examples/pagination" },
{ "label": "Row DnD", "to": "framework/react/examples/row-dnd" },
{ "label": "Row Pinning", "to": "framework/react/examples/row-pinning" },
{ "label": "Row Selection", "to": "framework/react/examples/row-selection" },
{ "label": "Sorting", "to": "framework/react/examples/sorting" }
]
},
{
"label": "solid",
"children": [
{ "label": "Kitchen Sink (All Features)", "to": "framework/solid/examples/kitchen-sink" },
{ "label": "Cell Selection", "to": "framework/solid/examples/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/solid/examples/cell-spanning" },
{ "label": "Column Filters", "to": "framework/solid/examples/filters" },
{ "label": "Column Filters (Faceted)", "to": "framework/solid/examples/filters-faceted" },
{ "label": "Bucketed Faceted Filters", "to": "framework/solid/examples/filters-faceted-bucketed" },
{ "label": "Fuzzy Search Filters", "to": "framework/solid/examples/filters-fuzzy" },
{ "label": "Column Ordering", "to": "framework/solid/examples/column-ordering" },
{ "label": "Column Pinning", "to": "framework/solid/examples/column-pinning" },
{ "label": "Column Pinning (Split)", "to": "framework/solid/examples/column-pinning-split" },
{ "label": "Sticky Column Pinning", "to": "framework/solid/examples/column-pinning-sticky" },
{ "label": "Column Sizing", "to": "framework/solid/examples/column-sizing" },
{ "label": "Column Resizing", "to": "framework/solid/examples/column-resizing" },
{ "label": "Performant Column Resizing", "to": "framework/solid/examples/column-resizing-performant" },
{ "label": "Column Visibility", "to": "framework/solid/examples/column-visibility" },
{ "label": "Expanding", "to": "framework/solid/examples/expanding" },
{ "label": "Expanding Sub Components", "to": "framework/solid/examples/sub-components" },
{ "label": "Grouping", "to": "framework/solid/examples/grouping" },
{ "label": "Aggregation", "to": "framework/solid/examples/aggregation" },
{ "label": "Grouped Aggregation", "to": "framework/solid/examples/grouped-aggregation" },
{ "label": "Pagination", "to": "framework/solid/examples/pagination" },
{ "label": "Row Pinning", "to": "framework/solid/examples/row-pinning" },
{ "label": "Row Selection", "to": "framework/solid/examples/row-selection" },
{ "label": "Sorting", "to": "framework/solid/examples/sorting" }
]
},
{
"label": "svelte",
"children": [
{ "label": "Kitchen Sink (All Features)", "to": "framework/svelte/examples/kitchen-sink" },
{ "label": "Cell Selection", "to": "framework/svelte/examples/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/svelte/examples/cell-spanning" },
{ "label": "Column Filters", "to": "framework/svelte/examples/filtering" },
{ "label": "Column Filters (Faceted)", "to": "framework/svelte/examples/filters-faceted" },
{ "label": "Bucketed Faceted Filters", "to": "framework/svelte/examples/filters-faceted-bucketed" },
{ "label": "Fuzzy Search Filters", "to": "framework/svelte/examples/filters-fuzzy" },
{ "label": "Column Ordering", "to": "framework/svelte/examples/column-ordering" },
{ "label": "Column Pinning", "to": "framework/svelte/examples/column-pinning" },
{ "label": "Column Pinning (Split)", "to": "framework/svelte/examples/column-pinning-split" },
{ "label": "Sticky Column Pinning", "to": "framework/svelte/examples/column-pinning-sticky" },
{ "label": "Column Sizing", "to": "framework/svelte/examples/column-sizing" },
{ "label": "Column Resizing", "to": "framework/svelte/examples/column-resizing" },
{ "label": "Performant Column Resizing", "to": "framework/svelte/examples/column-resizing-performant" },
{ "label": "Column Visibility", "to": "framework/svelte/examples/column-visibility" },
{ "label": "Expanding", "to": "framework/svelte/examples/expanding" },
{ "label": "Expanding Sub Components", "to": "framework/svelte/examples/sub-components" },
{ "label": "Grouping", "to": "framework/svelte/examples/grouping" },
{ "label": "Aggregation", "to": "framework/svelte/examples/aggregation" },
{ "label": "Grouped Aggregation", "to": "framework/svelte/examples/grouped-aggregation" },
{ "label": "Pagination", "to": "framework/svelte/examples/pagination" },
{ "label": "Row Pinning", "to": "framework/svelte/examples/row-pinning" },
{ "label": "Row Selection", "to": "framework/svelte/examples/row-selection" },
{ "label": "Sorting", "to": "framework/svelte/examples/sorting" }
]
},
{
"label": "vue",
"children": [
{ "label": "Kitchen Sink (All Features)", "to": "framework/vue/examples/kitchen-sink" },
{ "label": "Cell Selection", "to": "framework/vue/examples/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/vue/examples/cell-spanning" },
{ "label": "Column Filters", "to": "framework/vue/examples/filters" },
{ "label": "Column Filters (Faceted)", "to": "framework/vue/examples/filters-faceted" },
{ "label": "Bucketed Faceted Filters", "to": "framework/vue/examples/filters-faceted-bucketed" },
{ "label": "Fuzzy Search Filters", "to": "framework/vue/examples/filters-fuzzy" },
{ "label": "Column Ordering", "to": "framework/vue/examples/column-ordering" },
{ "label": "Column Pinning", "to": "framework/vue/examples/column-pinning" },
{ "label": "Column Pinning (Split)", "to": "framework/vue/examples/column-pinning-split" },
{ "label": "Sticky Column Pinning", "to": "framework/vue/examples/column-pinning-sticky" },
{ "label": "Column Sizing", "to": "framework/vue/examples/column-sizing" },
{ "label": "Column Resizing", "to": "framework/vue/examples/column-resizing" },
{ "label": "Performant Column Resizing", "to": "framework/vue/examples/column-resizing-performant" },
{ "label": "Column Visibility", "to": "framework/vue/examples/column-visibility" },
{ "label": "Expanding", "to": "framework/vue/examples/expanding" },
{ "label": "Expanding Sub Components", "to": "framework/vue/examples/sub-components" },
{ "label": "Grouping", "to": "framework/vue/examples/grouping" },
{ "label": "Aggregation", "to": "framework/vue/examples/aggregation" },
{ "label": "Grouped Aggregation", "to": "framework/vue/examples/grouped-aggregation" },
{ "label": "Pagination", "to": "framework/vue/examples/pagination" },
{ "label": "Row Pinning", "to": "framework/vue/examples/row-pinning" },
{ "label": "Row Selection", "to": "framework/vue/examples/row-selection" },
{ "label": "Sorting", "to": "framework/vue/examples/sorting" }
]
},
{
"label": "preact",
"children": [
{ "label": "Kitchen Sink (All Features)", "to": "framework/preact/examples/kitchen-sink" },
{ "label": "Cell Selection", "to": "framework/preact/examples/cell-selection" },
{ "label": "Cell Spanning", "to": "framework/preact/examples/cell-spanning" },
{ "label": "Column Filters", "to": "framework/preact/examples/filters" },
{ "label": "Column Filters (Faceted)", "to": "framework/preact/examples/filters-faceted" },
{ "label": "Bucketed Faceted Filters", "to": "framework/preact/examples/filters-faceted-bucketed" },
{ "label": "Fuzzy Search Filters", "to": "framework/preact/examples/filters-fuzzy" },
{ "label": "Column Ordering", "to": "framework/preact/examples/column-ordering" },
{ "label": "Column Pinning", "to": "framework/preact/examples/column-pinning" },
{ "label": "Column Pinning (Split)", "to": "framework/preact/examples/column-pinning-split" },
{ "label": "Sticky Column Pinning", "to": "framework/preact/examples/column-pinning-sticky" },
{ "label": "Column Sizing", "to": "framework/preact/examples/column-sizing" },
{ "label": "Column Resizing", "to": "framework/preact/examples/column-resizing" },
{ "label": "Performant Column Resizing", "to": "framework/preact/examples/column-resizing-performant" },
{ "label": "Column Visibility", "to": "framework/preact/examples/column-visibility" },
{ "label": "Expanding", "to": "framework/preact/examples/expanding" },
{ "label": "Expanding Sub Components", "to": "framework/preact/examples/sub-components" },
{ "label": "Grouping", "to": "framework/preact/examples/grouping" },
{ "label": "Aggregation", "to": "framework/preact/examples/aggregation" },
{ "label": "Grouped Aggregation", "to": "framework/preact/examples/grouped-aggregation" },
{ "label": "Pagination", "to": "framework/preact/examples/pagination" },
{ "label": "Row Pinning", "to": "framework/preact/examples/row-pinning" },
{ "label": "Row Selection", "to": "framework/preact/examples/row-selection" },
{ "label": "Sorting", "to": "framework/preact/examples/sorting" }
]
},
{
"label": "octane",
"children": [
{ "label": "Kitchen Sink (All Features)", "to": "framework/octane/examples/kitchen-sink" },
{ "label": "Cell Spanning", "to": "framework/octane/examples/cell-spanning" },
{ "label": "Column Filters", "to": "framework/octane/examples/filters" },
{ "label": "Column Filters (Faceted)", "to": "framework/octane/examples/filters-faceted" },
{ "label": "Bucketed Faceted Filters", "to": "framework/octane/examples/filters-faceted-bucketed" },
{ "label": "Fuzzy Search Filters", "to": "framework/octane/examples/filters-fuzzy" },
{ "label": "Column Ordering", "to": "framework/octane/examples/column-ordering" },
{ "label": "Column Pinning", "to": "framework/octane/examples/column-pinning" },
{ "label": "Column Pinning (Split)", "to": "framework/octane/examples/column-pinning-split" },
{ "label": "Sticky Column Pinning", "to": "framework/octane/examples/column-pinning-sticky" },
{ "label": "Column Sizing", "to": "framework/octane/examples/column-sizing" },
{ "label": "Column Resizing", "to": "framework/octane/examples/column-resizing" },
{ "label": "Performant Column Resizing", "to": "framework/octane/examples/column-resizing-performant" },
{ "label": "Column Visibility", "to": "framework/octane/examples/column-visibility" },
{ "label": "Expanding", "to": "framework/octane/examples/expanding" },
{ "label": "Expanding Sub Components", "to": "framework/octane/examples/sub-components" },
{ "label": "Grouping", "to": "framework/octane/examples/grouping" },
{ "label": "Aggregation", "to": "framework/octane/examples/aggregation" },
{ "label": "Grouped Aggregation", "to": "framework/octane/examples/grouped-aggregation" },
{ "label": "Pagination", "to": "framework/octane/examples/pagination" },
{ "label": "Row Pinning", "to": "framework/octane/examples/row-pinning" },
{ "label": "Row Selection", "to": "framework/octane/examples/row-selection" },
{ "label": "Sorting", "to": "framework/octane/examples/sorting" }
]
},
{
"label": "vanilla",
"children": [
{ "label": "Aggregation", "to": "framework/vanilla/examples/aggregation" },
{ "label": "Pagination", "to": "framework/vanilla/examples/pagination" },
{ "label": "Sorting", "to": "framework/vanilla/examples/sorting" }
]
}
]
},
{
"label": "Specialized Examples",
"children": [],
"frameworks": [
{
"label": "alpine",
"children": [
{ "label": "Custom Plugin", "to": "framework/alpine/examples/custom-plugin" }
]
},
{
"label": "angular",
"children": [
{ "label": "Composable Tables (createTableHook)", "to": "framework/angular/examples/composable-tables" },
{ "label": "Custom Plugin", "to": "framework/angular/examples/custom-plugin" },
{ "label": "With TanStack Virtual - Columns", "to": "framework/angular/examples/virtualized-columns" },
{ "label": "With TanStack Virtual - Rows", "to": "framework/angular/examples/virtualized-rows" },
{ "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/angular/examples/virtualized-infinite-scrolling" },
{ "label": "With TanStack Form", "to": "framework/angular/examples/with-tanstack-form" },
{ "label": "With TanStack Query", "to": "framework/angular/examples/with-tanstack-query" },
{ "label": "Fetch API data (SPA / SSR)", "to": "framework/angular/examples/remote-data" },
{ "label": "Signal Input", "to": "framework/angular/examples/signal-input" },
{ "label": "Row Selection (Signal)", "to": "framework/angular/examples/row-selection-signal" },
{ "label": "Editable data", "to": "framework/angular/examples/editable" },
{ "label": "Row Drag & Drop", "to": "framework/angular/examples/row-dnd" }
]
},
{
"label": "ember",
"children": [
{ "label": "Custom Plugin", "to": "framework/ember/examples/custom-plugin" },
{ "label": "Editable data", "to": "framework/ember/examples/editable" },
{ "label": "Fetch API data (SPA / SSR)", "to": "framework/ember/examples/remote-data" },
{ "label": "Row Drag & Drop", "to": "framework/ember/examples/row-dnd" }
]
},
{
"label": "lit",
"children": [
{ "label": "Composable Tables (createTableHook)", "to": "framework/lit/examples/composable-tables" },
{ "label": "With TanStack Virtual - Columns", "to": "framework/lit/examples/virtualized-columns" },
{ "label": "With TanStack Virtual - Rows", "to": "framework/lit/examples/virtualized-rows" },
{ "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/lit/examples/virtualized-infinite-scrolling" }
]
},
{
"label": "react",
"children": [
{ "label": "Composable Tables (createTableHook)", "to": "framework/react/examples/composable-tables" },
{ "label": "Custom Plugin", "to": "framework/react/examples/custom-plugin" },
{ "label": "Experimental Web Workers Plugin", "to": "framework/react/examples/web-worker-row-models" },
{ "label": "Experimental Spreadsheet", "to": "framework/react/examples/spreadsheet" },
{ "label": "With TanStack Virtual - Columns", "to": "framework/react/examples/virtualized-columns" },
{ "label": "With TanStack Virtual - Columns (Exp)", "to": "framework/react/examples/virtualized-columns-experimental" },
{ "label": "With TanStack Virtual - Rows", "to": "framework/react/examples/virtualized-rows" },
{ "label": "With TanStack Virtual - Rows (Exp)", "to": "framework/react/examples/virtualized-rows-experimental" },
{ "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/react/examples/virtualized-infinite-scrolling" },
{ "label": "With TanStack Form", "to": "framework/react/examples/with-tanstack-form" },
{ "label": "With TanStack Query", "to": "framework/react/examples/with-tanstack-query" },
{ "label": "With TanStack Router", "to": "framework/react/examples/with-tanstack-router" }
]
},
{
"label": "solid",
"children": [
{ "label": "Composable Tables (createTableHook)", "to": "framework/solid/examples/composable-tables" },
{ "label": "Experimental Spreadsheet", "to": "framework/solid/examples/spreadsheet" },
{ "label": "With TanStack Virtual - Columns", "to": "framework/solid/examples/virtualized-columns" },
{ "label": "With TanStack Virtual - Rows", "to": "framework/solid/examples/virtualized-rows" },
{ "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/solid/examples/virtualized-infinite-scrolling" },
{ "label": "With TanStack Form", "to": "framework/solid/examples/with-tanstack-form" },
{ "label": "With TanStack Query", "to": "framework/solid/examples/with-tanstack-query" },
{ "label": "With TanStack Router", "to": "framework/solid/examples/with-tanstack-router" }
]
},
{
"label": "svelte",
"children": [
{ "label": "Composable Tables (createTableHook)", "to": "framework/svelte/examples/composable-tables" },
{ "label": "Experimental Spreadsheet", "to": "framework/svelte/examples/spreadsheet" },
{ "label": "With TanStack Virtual - Columns", "to": "framework/svelte/examples/virtualized-columns" },
{ "label": "With TanStack Virtual - Rows", "to": "framework/svelte/examples/virtualized-rows" },
{ "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/svelte/examples/virtualized-infinite-scrolling" },
{ "label": "With TanStack Form", "to": "framework/svelte/examples/with-tanstack-form" },
{ "label": "With TanStack Query", "to": "framework/svelte/examples/with-tanstack-query" }
]
},
{
"label": "vue",
"children": [
{ "label": "Composable Tables (createTableHook)", "to": "framework/vue/examples/composable-tables" },
{ "label": "With TanStack Virtual - Columns", "to": "framework/vue/examples/virtualized-columns" },
{ "label": "With TanStack Virtual - Rows", "to": "framework/vue/examples/virtualized-rows" },
{ "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/vue/examples/virtualized-infinite-scrolling" },
{ "label": "With TanStack Form", "to": "framework/vue/examples/with-tanstack-form" },
{ "label": "With TanStack Query", "to": "framework/vue/examples/with-tanstack-query" }
]
},
{
"label": "preact",
"children": [
{ "label": "Composable Tables (createTableHook)", "to": "framework/preact/examples/composable-tables" },
{ "label": "Custom Plugin", "to": "framework/preact/examples/custom-plugin" },
{ "label": "With TanStack Query", "to": "framework/preact/examples/with-tanstack-query" }
]
},
{
"label": "octane",
"children": [
{ "label": "Composable Tables (createTableHook)", "to": "framework/octane/examples/composable-tables" },
{ "label": "Custom Plugin", "to": "framework/octane/examples/custom-plugin" }
]
}
]
},
{
"label": "Component Library Examples",
"children": [],
"frameworks": [
{
"label": "react",
"children": [
{ "label": "Basic - Shadcn (Base UI)", "to": "framework/react/examples/lib-shadcn-base" },
{ "label": "Basic - Shadcn (Radix)", "to": "framework/react/examples/lib-shadcn-radix" },
{ "label": "Basic - React Aria", "to": "framework/react/examples/lib-react-aria" },
{ "label": "Basic - Hero UI", "to": "framework/react/examples/lib-hero-ui" },
{ "label": "Basic - Material UI", "to": "framework/react/examples/lib-material-ui" },
{ "label": "Basic - Mantine", "to": "framework/react/examples/lib-mantine" },
{ "label": "Basic - Chakra UI", "to": "framework/react/examples/lib-chakra-ui" },
{ "label": "Kitchen Sink - Shadcn (Base UI)", "to": "framework/react/examples/kitchen-sink-shadcn-base" },
{ "label": "Kitchen Sink - Shadcn (Radix)", "to": "framework/react/examples/kitchen-sink-shadcn-radix" },
{ "label": "Kitchen Sink - React Aria", "to": "framework/react/examples/kitchen-sink-react-aria" },
{ "label": "Kitchen Sink - Hero UI", "to": "framework/react/examples/kitchen-sink-hero-ui" },
{ "label": "Kitchen Sink - Material UI", "to": "framework/react/examples/kitchen-sink-material-ui" },
{ "label": "Kitchen Sink - Mantine", "to": "framework/react/examples/kitchen-sink-mantine" },
{ "label": "Kitchen Sink - Chakra UI", "to": "framework/react/examples/kitchen-sink-chakra-ui" },
{ "label": "Material React Table", "to": "framework/react/examples/material-react-table" },
{ "label": "Mantine React Table", "to": "framework/react/examples/mantine-react-table" }
]
},
{
"label": "svelte",
"children": [
{ "label": "Basic - Shadcn", "to": "framework/svelte/examples/lib-shadcn" },
{ "label": "Kitchen Sink - Shadcn", "to": "framework/svelte/examples/kitchen-sink-shadcn" }
]
},
{
"label": "vue",
"children": [
{ "label": "Basic - Shadcn", "to": "framework/vue/examples/lib-shadcn" },
{ "label": "Kitchen Sink - Shadcn", "to": "framework/vue/examples/kitchen-sink-shadcn" }
]
}
]
}
]
}
Devtools (`docs/devtools.md`)
title: Devtools id: devtools
TanStack Table provides framework-specific devtools adapters that plug into the TanStack Devtools multi-panel UI.
The table devtools let you inspect registered table instances, switch between multiple tables, and inspect features, state, options, rows, and columns in real time.
[!NOTE]
By default, the framework adapters only include the live devtools in development mode. In production builds they export no-op implementations unless you opt into the/productionentrypoints.
Installation
Install the TanStack Devtools host package and the Table adapter for your framework.
React
npm install @tanstack/react-devtools @tanstack/react-table-devtoolsPreact
npm install @tanstack/preact-devtools @tanstack/preact-table-devtoolsOctane
There is not currently a dedicated Octane Table Devtools adapter.
Vue
npm install @tanstack/vue-devtools @tanstack/vue-table-devtoolsSolid
npm install @tanstack/solid-devtools @tanstack/solid-table-devtoolsAngular
npm install @tanstack/angular-devtools @tanstack/angular-table-devtoolsOctane, Lit, Svelte, Alpine, and vanilla do not currently ship dedicated table devtools adapters.
The Required `key` Table Option
The devtools identify each table by the key table option. Registration requires it. If you register a table without a key, the devtools log an error (Missing table key. Add a 'key' option to your table to use devtools.) and skip the table entirely.
const table = useTable({
key: 'users-table', // needed for devtools, omit if you don't want to use the devtools
features,
columns,
data,
})The key is also the label shown in the devtools panel selector, so give each table a unique, descriptive key.
Setup Pattern
The recommended setup has three parts:
- Give each table a unique
keyoption - Mount
TanStackDevtoolsat the app root withtableDevtoolsPlugin() - Register each table with
useTanStackTableDevtools(table)(orinjectTanStackTableDevtoolsin Angular) immediately after creating it
If you register multiple tables, the Table panel shows a selector so you can switch between them.
Setup
React
import React from 'react'
import ReactDOM from 'react-dom/client'
import { useTable } from '@tanstack/react-table'
import { TanStackDevtools } from '@tanstack/react-devtools'
import {
tableDevtoolsPlugin,
useTanStackTableDevtools,
} from '@tanstack/react-table-devtools'
function App() {
const table = useTable({
key: 'users-table', // needed for devtools
// ...
})
useTanStackTableDevtools(table)
return <AppContent table={table} />
}
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
<TanStackDevtools plugins={[tableDevtoolsPlugin()]} />
</React.StrictMode>,
)See the React row-selection example.
Preact
import { render } from 'preact'
import { useTable } from '@tanstack/preact-table'
import { TanStackDevtools } from '@tanstack/preact-devtools'
import {
tableDevtoolsPlugin,
useTanStackTableDevtools,
} from '@tanstack/preact-table-devtools'
function App() {
const table = useTable({
key: 'users-table', // needed for devtools
// ...
})
useTanStackTableDevtools(table)
return <AppContent table={table} />
}
render(
<>
<App />
<TanStackDevtools plugins={[tableDevtoolsPlugin()]} />
</>,
document.getElementById('root')!,
)Octane
Octane tables expose the same table.state, slice atoms, table.store, and table.Subscribe inspection surfaces, but there is not currently an Octane Table Devtools plugin to register.
See the Preact row-selection example.
Vue
// main.ts
import { createApp, defineComponent, h } from 'vue'
import { TanStackDevtools } from '@tanstack/vue-devtools'
import { tableDevtoolsPlugin } from '@tanstack/vue-table-devtools'
import App from './App.vue'
const Root = defineComponent({
setup() {
return () => [
h(App),
h(TanStackDevtools, {
plugins: [tableDevtoolsPlugin({})],
}),
]
},
})
createApp(Root).mount('#app')<script setup lang="ts">
import { useTable } from '@tanstack/vue-table'
import { useTanStackTableDevtools } from '@tanstack/vue-table-devtools'
const table = useTable({
key: 'users-table', // needed for devtools
// ...
})
useTanStackTableDevtools(table)
</script>See the Vue row-selection example.
Solid
import { render } from 'solid-js/web'
import { createTable } from '@tanstack/solid-table'
import { TanStackDevtools } from '@tanstack/solid-devtools'
import {
tableDevtoolsPlugin,
useTanStackTableDevtools,
} from '@tanstack/solid-table-devtools'
function App() {
const table = createTable({
key: 'users-table', // needed for devtools
// ...
})
useTanStackTableDevtools(table)
return <AppContent table={table} />
}
render(
() => (
<>
<App />
<TanStackDevtools plugins={[tableDevtoolsPlugin()]} />
</>
),
document.getElementById('root')!,
)See the Solid row-selection example.
Angular
Provide the devtools host once in your application config, rendering the table panel from @tanstack/angular-table-devtools:
// app.config.ts
import { isDevMode } from '@angular/core'
import { provideTanStackDevtools } from '@tanstack/angular-devtools/provider'
import type { ApplicationConfig } from '@angular/core'
export const appConfig: ApplicationConfig = {
providers: [
isDevMode()
? provideTanStackDevtools(() => ({
plugins: [
{
name: 'TanStack Table',
render: () =>
import('@tanstack/angular-table-devtools').then((m) =>
m.TableDevtoolsPanel(),
),
},
],
}))
: [],
],
}Then register each table with injectTanStackTableDevtools in an injection context (such as a component constructor):
import { Component } from '@angular/core'
import { injectTable } from '@tanstack/angular-table'
import { injectTanStackTableDevtools } from '@tanstack/angular-table-devtools'
@Component({
// ...
})
export class App {
constructor() {
injectTanStackTableDevtools(() => ({
table: this.table,
}))
}
readonly table = injectTable(() => ({
key: 'users-table', // needed for devtools
// ...
}))
}See the Angular basic example or the Angular row-selection example.
Disabling Registration
Each registration function accepts an enabled option if you want to conditionally register a table:
React
useTanStackTableDevtools(table, { enabled: false })Preact
useTanStackTableDevtools(table, { enabled: false })Octane
No adapter toggle is required because Octane does not currently ship a Table Devtools adapter.
Vue
useTanStackTableDevtools(table, { enabled: false })Solid
useTanStackTableDevtools(table, { enabled: false })Angular
injectTanStackTableDevtools(() => ({
table: this.table,
enabled: () => false,
}))Production Builds
If you need the live devtools in production, import from the /production entrypoint for your framework package:
React
import {
tableDevtoolsPlugin,
useTanStackTableDevtools,
} from '@tanstack/react-table-devtools/production'Preact
import {
tableDevtoolsPlugin,
useTanStackTableDevtools,
} from '@tanstack/preact-table-devtools/production'Octane
There is no production Devtools entry point for Octane Table at this time.
Vue
import {
tableDevtoolsPlugin,
useTanStackTableDevtools,
} from '@tanstack/vue-table-devtools/production'Solid
import {
tableDevtoolsPlugin,
useTanStackTableDevtools,
} from '@tanstack/solid-table-devtools/production'Angular
import { injectTanStackTableDevtools } from '@tanstack/angular-table-devtools/production'Installation (`docs/installation.md`)
title: Installation
Before we dig in to the API, let's get you set up!
Install your table adapter as a dependency using your preferred package manager:
react: @tanstack/react-table
preact: @tanstack/preact-table
octane: @tanstack/octane-table
vue: @tanstack/vue-table
solid: @tanstack/solid-table
svelte: @tanstack/svelte-table
angular: @tanstack/angular-table
ember: @tanstack/ember-table
lit: @tanstack/lit-table
alpine: @tanstack/alpine-table
React
The @tanstack/react-table package works with React 18 or newer.
Preact
The @tanstack/preact-table package works with Preact 10 or newer.
Octane
The @tanstack/octane-table package works with Octane 0.1.21. It publishes authored TypeScript and TSRX, so your application must use Octane's compiler integration (for example, the Octane Vite plugin) to compile the package for its current target and mode.
Vue
The @tanstack/vue-table package works with Vue 3.2 or newer.
Solid
The @tanstack/solid-table package works with Solid 1.3 or newer.
Svelte
The @tanstack/svelte-table package works with Svelte 5 (it is built on runes). For Svelte 3/4 support, use TanStack Table v8.
Angular
The @tanstack/angular-table package works with Angular 19 or newer. The Angular adapter is built on Angular Signals.
Ember
The @tanstack/ember-table package is a v2 addon that works with Ember 5.8 or newer (Embroider or ember-auto-import v2). It is built on Glimmer's tracking system and supports .gts/.gjs template tag components and Glint.
Lit
The @tanstack/lit-table package works with Lit 3 (3.1.3 or newer) and also requires @lit/context as a peer dependency.
Alpine
The @tanstack/alpine-table package works with Alpine 3.
Don't see your favorite framework (or favorite version of your framework) listed? You can always just use the @tanstack/table-core package and build your own adapter in your own codebase. Usually, only a thin wrapper is needed to manage state and rendering for your specific framework. Browse the source code of all of the other adapters to see how they work.
Guide Cells (`docs/guide/cells.md`)
title: Cells Guide
This quick guide will discuss the different ways you can retrieve and interact with cell objects in TanStack Table.
Where to Get Cells From
Cells come from Rows. Enough said, right?
There are multiple row instance APIs you can use to retrieve the appropriate cells from a row depending on which features you are using. Most commonly, you will use the row.getAllCells or row.getVisibleCells APIs (if you are using column visibility features), but there are a handful of other similar APIs that you can use.
Cell Objects
Every cell object can be associated with a `` or similar cell element in your UI. There are a few properties and methods on cell objects that you can use to interact with the table state and extract cell values from the table based on the state of the table.
Cell IDs
Every cell object has an id property that makes it unique within the table instance. Each cell.id is constructed simply as a union of its parent row and column IDs separated by an underscore.
{
id: `${row.id}_${column.id}`
}During grouping or aggregation features, the cell.id will have an additional string appended to it.
Cell Parent Objects
Every cell stores a reference to its parent row and column objects.
Access Cell Values
The recommended way to access data values from a cell is to use either the cell.getValue or cell.renderValue APIs. Using either of these APIs will cache the results of the accessor functions and keep rendering efficient. The only difference between the two is that cell.renderValue will return either the value or the renderFallbackValue if the value is undefined, whereas cell.getValue will return the value or undefined if the value is undefined.
[!NOTE]
Thecell.getValueandcell.renderValueAPIs are shortcuts for therow.getValueandrow.renderValueAPIs, respectively.
// Access the value for this cell's own column
const value = cell.getValue() // read the cell value
const renderedValue = cell.renderValue() // read the cell value, falling back to `renderFallbackValue`These APIs take no arguments. They are bound to the cell's own column. If you need to read a different column's value from within a cell, use the parent row instead, e.g. cell.row.getValue('someColumnId').
Access Other Row Data from Any Cell
Since every cell object is associated with its parent row, you can access any data from the original row that you are using in your table using cell.row.original.
// Even if we are in the scope of a different cell, we can still access the original row data
const firstName = cell.row.original.firstName // { firstName: 'John', lastName: 'Doe' }More Cell APIs
Depending on the features that you are using for your table, there are dozens more useful APIs for interacting with cells. See each feature's respective API docs or guide for more information.
Cell Rendering
You can just use the cell.renderValue or cell.getValue APIs to render the cells of your table. However, these APIs will only spit out the raw cell values (from accessor functions). If you are using the cell: () => JSX column definition options, you will want to use the flexRender API utility from your adapter.
The flexRender API renders the cell correctly with any extra markup or JSX and calls the callback function with the correct parameters.
import { flexRender } from '@tanstack/react-table'
const columns = [
{
accessorKey: 'fullName',
cell: ({ cell, row }) => {
return <div><strong>{row.original.firstName}</strong> {row.original.lastName}</div>
}
//...
}
]
//...
<tr>
{row.getVisibleCells().map(cell => {
return <td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
})}
</tr>Cell Spanning
When the optional cellSpanningFeature is registered, adjacent cells can merge into one rendered cell. A cell reporting a span of 0 is covered by another cell's span and must be skipped instead of rendered. See the Cell Spanning Guide for the full rendering pattern.
Guide Client Side Vs Server Side (`docs/guide/client-side-vs-server-side.md`)
title: Client-Side vs Server-Side Guide
Examples
React
Preact
Vue
Solid
Svelte
Angular
[!IMPORTANT]
TanStack Table supports both client-side and server-side row processing!
More accurately, TanStack Table lets you bring your own backend or other manual approach to processing data for features such as filtering, grouping, sorting, expanding, aggregating, faceting, and pagination.
TanStack Table can filter, group, sort, expand, aggregate, facet, and paginate rows in the browser through client-side row model processing. It can also just manage the state for those features while your server performs the actual data processing. The right approach depends on how much data the browser receives, how expensive that data is to transfer and process, and the experience you want to provide.
Whichever option you choose, you pretty much have to choose just one or the other. You can't really mix and match client-side and server-side row processing for different features. This guide covers that shared decision. The individual feature guides explain the options and row models for implementing each feature.
Start With the Simplest Approach That Fits
Client-side processing is usually the simplest approach. This is where you let TanStack Table do that processing for you. Fetch the complete dataset for the table, pass it to TanStack Table, and enable the row models you need. Filtering, sorting, and pagination can then update immediately without another request.
You might be tempted to rule out client-side processing based on what you think is a large dataset, but tables with just a few thousand rows are often practical in the browser. TanStack Table examples stress-test much larger datasets. In fact, we stress-test all TanStack Table features with 1 million rows client-side and expect usable performance. Actual performance depends on the number of columns, the size and shape of each row, the work performed by accessors and feature functions, and the devices your users have. Test with representative data and target hardware.
Older versions of TanStack Table used to start running into memory issues at about 1 million rows, but thanks to our Object Prototypes Refactor we can now claim to support up to 15 million rows client-side with ease. Is loading 15 million rows practical for your use-case? Probably not! But we hope you know that TanStack Table should give you headroom for many client-side rows.
Server-side processing is usually a better fit when:
- fetching the complete dataset would be slow, expensive, or memory-intensive;
- the browser only receives a page or another subset of the rows;
- queries, permissions, or business rules must be enforced by the backend;
- data changes frequently enough that downloading it all would become stale; or
- the backend can perform indexed searching, sorting, grouping, or aggregation more efficiently.
When evaluating the tradeoff, measure:
- How long and how much it costs the server to query the complete dataset.
- The transferred payload size, not only the number of rows.
- Browser memory usage and the time spent computing row models.
- The interaction cost of waiting for a request after each state change.
If both approaches fit today, starting on the client keeps the data flow smaller. Owning the relevant table state makes it easier to move processing to the server later.
Keep Dataset-Wide Operations Consistent
Filtering, grouping, sorting, and pagination usually describe one pipeline over the same dataset. If the server sends only part of that dataset, a client-side operation can only process the loaded rows.
For example, client-side sorting after server-side pagination sorts the current page, not every matching row. Client-side filtering after server-side pagination can hide rows on the current page without finding matches on other pages. These results are usually misleading.
As a rule, when the server owns pagination, it should also own any filtering, grouping, sorting, or aggregation that must apply to the full result set. Mixing approaches is valid when the smaller scope is intentional. For example, you might rank the rows within each server-provided group. Make that scope clear in the UI.
Facets need the same consideration. Facet counts calculated from a server-provided page describe only that page. Calculate facets on the server when they need to represent the full filtered dataset.
What "Manual" Means
TanStack Table calls server-side data processing "manual" because the table does not perform that transformation. A manual* option does not fetch or transform data. It tells the table to use the data you provide as already processed for that feature.
| Operation | Manual option | Client-side row model or implementation |
|---|---|---|
| Column and global filtering | manualFiltering |
filteredRowModel |
| Grouping | manualGrouping |
groupedRowModel |
| Aggregation values | manualAggregation |
aggregationFn local fallback |
| Sorting | manualSorting |
sortedRowModel |
| Expanding | manualExpanding |
expandedRowModel |
| Pagination | manualPagination |
paginatedRowModel |
Faceting also supports server-provided results, but it uses custom factories rather than a manual* option. See the Aggregation and Faceting guides for those feature-specific details.
You can omit an unused client-side row model. If a shared table configuration includes one, its matching manual* option tells the table to bypass it. The feature itself can remain enabled so the table still supplies its state and APIs.
A Typical Server-Side Data Flow
With server-side processing, you write the filtering, grouping, sorting, aggregation, and pagination logic in the backend language, SQL query layer, database API, or service architecture that fits your application.
TanStack Table also does not fetch data. Your application is responsible for sending the table state to the backend and providing the returned rows to the table. You can use any data-fetching solution you prefer. TanStack Query is an excellent companion when you want declarative fetching, caching, loading states, and request lifecycle management, and it composes naturally with controlled TanStack Table state.
For server-side processing:
- Own the relevant filter, grouping, sorting, and pagination state so your data-fetching code can read it.
- Include every server-owned state value in the request or query key.
- Enable the matching
manual*options. - Pass the processed rows returned by the server to
data. - For manual pagination, also provide
rowCountorpageCountwhen known. - Reset or validate the page index when filters, grouping, sorting, or page size change.
- Keep previous results or show loading state deliberately, and prevent slower stale responses from replacing newer results.
manualPagination disables autoResetPageIndex by default.
More generally, auto-reset side effects run when a row model that invokes them recomputes. Client-side filtering, grouping, and sorting row models normally invoke page-index resets after their related state changes. In a fully manual server-side configuration that omits those row models, controlled state changes do not run their reset hooks. Reset dependent state in the corresponding change callbacks instead, as shown below. Core data changes can still trigger some auto-reset behaviors when returned data is processed.
The table continues to manage state and expose event handlers; your application connects that state to its data-fetching layer. See the framework-specific Table State guide and the With TanStack Query example for complete patterns:
React
Preact
Octane
Vue
Solid
Svelte
Angular
Ember
Lit
Alpine
Vanilla
Use a stable backend identifier with getRowId when selection, expansion, or other row state must survive requests. Page-relative row indexes do not identify the same record reliably across server responses.
Why No Built-In Server Row Models
TanStack Table is a synchronous table state manager. Its client-side row models synchronously transform data that is already in memory, while its state and APIs describe the filtering, sorting, and pagination the user wants. Fetching data and running backend queries happen outside of the table.
There are countless valid ways to design a backend and the contract between a frontend and backend: REST query parameters, GraphQL inputs, RPC calls, SQL builders, database SDKs, streaming responses, and more. A built-in server row model would have to impose opinions about those choices. TanStack Table deliberately leaves that boundary to your application so it can work with any backend stack or API design.
In practice, TanStack Query is the client-side coordinator for server-side row processing. Your own fetching layer can fill the same role. It observes the controlled table state, sends that state through your API contract, caches the result, and gives the returned rows back to the table. For example, a React integration can pass every server-owned state value to both the query key and query function:
Page-Index Pagination with `useQuery`
/* Detailed source-code truncated for AI context efficiency. */Here, fetchPeople defines your application's frontend-backend contract. The backend remains responsible for applying the operations in the correct order and returning the requested rows plus the total filtered rowCount.
Cursor-Based Pagination with `useInfiniteQuery`
Cursor pagination is a useful alternative when calculating a total row count or supporting arbitrary page jumps would be expensive. Instead of accepting a page index, the backend accepts a cursor and returns the current rows, a nextCursor, and whether another page exists. A production cursor is often opaque; a stable last-row ID can also work when IDs are unique and the backend ordering is deterministic.
TanStack Query's useInfiniteQuery caches the cursor chain. TanStack Table can use pagination.pageIndex to select one of those cached pages while the application fetches a new page only when the user advances beyond the cache:
const dataQuery = useInfiniteQuery({
queryKey: ['people', 'cursor', pagination.pageSize, sorting, globalFilter],
queryFn: ({ pageParam }) =>
fetchPeople({
cursor: pageParam,
pageSize: pagination.pageSize,
sorting,
globalFilter,
}),
initialPageParam: null,
getNextPageParam: (lastPage) => lastPage.nextCursor,
})
const currentPage = dataQuery.data?.pages[pagination.pageIndex]
const hasCachedNextPage = Boolean(
dataQuery.data?.pages[pagination.pageIndex + 1],
)
const canNextPage = hasCachedNextPage || Boolean(currentPage?.hasNextPage)
const table = useTable(
{
features,
columns,
data: currentPage?.rows ?? [],
pageCount: -1, // the cursor API does not expose a total page count
state: { sorting, globalFilter, pagination },
onSortingChange: (updater) => {
setSorting(updater)
setPagination((previous) => ({ ...previous, pageIndex: 0 }))
},
onGlobalFilterChange: (updater) => {
setGlobalFilter(updater)
setPagination((previous) => ({ ...previous, pageIndex: 0 }))
},
onPaginationChange: setPagination,
manualFiltering: true,
manualSorting: true,
manualPagination: true,
},
(state) => state,
)
async function goToNextPage() {
const nextPageIndex = pagination.pageIndex + 1
if (!dataQuery.data?.pages[nextPageIndex]) {
const result = await dataQuery.fetchNextPage()
if (!result.data?.pages[nextPageIndex]) return
}
table.nextPage()
}Use canNextPage for the Next button. With pageCount: -1, the table's getCanNextPage() cannot know when the server has reached the end. Previously fetched pages can still be revisited without another request. Because the total is unknown, there is no finite page for lastPage() to target, so getCanLastPage() returns false. Reset pageIndex to 0 when sorting, filtering, or page size changes so the new query starts from its initial cursor.
Rendering Is a Separate Decision
Data processing and rendering solve different problems:
- Pagination limits how many rows appear at once and can be performed on either the client or server.
- Virtualization renders only the visible portion of the rows already loaded in the browser.
Virtualization can make a large client-side dataset inexpensive to render, but it does not reduce the amount of data fetched or the work needed to filter and sort that dataset. If the complete dataset is too large to load, use server-side operations or incremental fetching; then add virtualization if the loaded result is still large enough to make rendering expensive.
Choosing an Approach
Use client-side processing when the browser can reasonably fetch and retain the complete dataset and immediate local interactions are valuable. Use server-side processing when the complete dataset should not or cannot be loaded, or when the backend must define the authoritative result.
Choosing server-side processing means bringing your own implementation. TanStack Table supplies the state and APIs that describe what the user wants, but your backend code or SQL must process that state, and your data-fetching code must transport the request and response. No TanStack Table server-side model or built-in fetching layer is involved. This separation keeps TanStack Table compatible with any backend. TanStack Query is an optional companion when you want help managing the fetching and caching layer.
Whichever approach you choose, test the full pipeline with realistic data. Network transfer, browser memory, row-model computation, DOM rendering, and backend query cost are separate bottlenecks, and the best boundary is the one that keeps all of them acceptable for your users.
Guide Column Defs (`docs/guide/column-defs.md`)
title: Column Definitions Guide
[!NOTE]
This guide is about setting up column definitions for your table and NOT about the actualcolumnobjects that are generated within the table instance.
Column defs are the single most important part of building a table. They are responsible for:
- Building the underlying data model that will be used for everything including sorting, filtering, grouping, etc.
- Formatting the data model into what will be displayed in the table
- Creating header groups, headers and footers
- Creating columns for display-only purposes, e.g. action buttons, checkboxes, expanders, sparklines, etc.
Column Def Types
The following "types" of column defs aren't actually TypeScript types, but more so a way to talk about and describe overall categories of column defs:
Accessor Columns- Accessor columns have an underlying data model which means they can be sorted, filtered, grouped, etc.
Display Columns- Display columns do not have a data model which means they cannot be sorted, filtered, etc, but they can be used to display arbitrary content in the table, e.g. a row actions button, checkbox, expander, etc.
Grouping Columns- Group columns do not have a data model so they too cannot be sorted, filtered, etc, and are used to group other columns together. It's common to define a header or footer for a column group.
Column Helpers
While column defs are just plain objects at the end of the day, a createColumnHelper function is exposed from the table core which, when called with your features type and row type, returns a utility for creating different column definition types with the highest type-safety possible.
In v9, createColumnHelper requires two type parameters: TFeatures (from your features object) and TData (your row type). Use typeof features to get the features type.
[!NOTE]
If you use thecreateTableHookfactory, it returns acreateAppColumnHelperthat is already bound to your features type, so you only passTData. See the Composable Tables Guide.
Here's an example of creating and using a column helper:
/* Detailed source-code truncated for AI context efficiency. */Creating Accessor Columns
Accessor columns are unique in that they must be configured to extract primitive values for each item in your data array.
There are 3 ways to do this:
- If your items are
objects, use an object-key that corresponds to the value you want to extract. - If your items are nested
arrays, use an array index that corresponds to the value you want to extract. - Use an accessor function that returns the value you want to extract.
Object Keys
If each of your items is an object with the following shape:
type Person = {
firstName: string
lastName: string
age: number
visits: number
status: string
progress: number
}You could extract the firstName value like so:
columnHelper.accessor('firstName')
// OR
{
accessorKey: 'firstName',
}Deep Keys
If each of your items is an object with the following shape:
type Person = {
name: {
first: string
last: string
}
info: {
age: number
visits: number
}
}You could extract the first value like so:
columnHelper.accessor('name.first', {
id: 'firstName',
})
// OR
{
accessorKey: 'name.first',
id: 'firstName',
}Array Indices
If each of your items is an array with the following shape:
type Sales = [Date, number]You could extract the number value like so:
columnHelper.accessor(row => row[1], {
id: 'amount',
})
// OR
{
accessorKey: '1', // string array index
}[!NOTE]
When usingaccessorKeywith array data, the key must be a string (e.g.'1', not the number1).
Accessor Functions
If each of your items is an object with the following shape:
type Person = {
firstName: string
lastName: string
age: number
visits: number
status: string
progress: number
}You could extract a computed full-name value like so:
columnHelper.accessor(row => `${row.firstName} ${row.lastName}`, {
id: 'fullName',
})
// OR
{
id: 'fullName',
accessorFn: row => `${row.firstName} ${row.lastName}`,
}🧠 Remember, the accessed value is what is used to sort, filter, etc. so you'll want to make sure your accessor function returns a primitive value that can be manipulated in a meaningful way. If you return a non-primitive value like an object or array, you will need the appropriate filter/sort/grouping functions to manipulate them, or even supply your own! 😬
Unique Column IDs
Columns are uniquely identified with 3 strategies:
- If defining an accessor column with an object key or array index, the same will be used to uniquely identify the column.
- Any periods (
.) in an object key will be replaced by underscores (_).
- Any periods (
- If defining an accessor column with an accessor function
- The column's
idproperty will be used to uniquely identify the column OR - If a primitive
stringheader is supplied, that header string will be used to uniquely identify the column
- The column's
🧠 An easy way to remember: If you define a column with an accessor function, either provide a string header or provide a unique
idproperty.
Dynamic Column Definitions
Column definitions do not have to be hard-coded. When the shape of your data is not known ahead of time (arbitrary API responses, user-uploaded CSV files, user-configurable reports), you can generate the column definitions from the data itself at runtime.
🧠 Naming note: accessor functions create computed ("derived") values within a known row shape, like the
fullNameexample above. Dynamic column definitions generate the column set itself when the row shape is unknown. The two techniques are complementary.
Since there is no static row type to infer from, type your rows as a generic record and build plain column def objects. The column helper offers no benefit here because there is no known shape for it to infer against:
type DynamicRow = Record<string, unknown>
const columns: Array<ColumnDef<typeof features, DynamicRow>> = data.length
? Object.keys(data[0]).map((key) => ({
accessorKey: key,
header: formatHeader(key), // e.g. 'firstName' -> 'First Name'
cell: (info) => String(info.getValue() ?? ''), // values are unknown, so coerce for rendering
}))
: []Two things to keep in mind:
- Stable identity. Rebuild the columns array only when the data actually changes (for example,
useMemokeyed ondatain React, or acomputed/memo in other frameworks). Recreating column defs on every render forces the table to reprocess all of its columns. - Runtime type detection. Since values are typed
unknown, inspect a sample value per key to choose type-appropriate options: asortFnandfilterFnsuited to numbers, dates, or booleans, and a matching filter UI. The registered function names described below make this a simple switch on the detected type.
See the Dynamic Columns example (available for every framework adapter) for a complete implementation with data type detection, per-type sort and filter functions, and per-type filter components powered by column faceting.
Column Formatting & Rendering
By default, column cells will display their data model value as a string. You can override this behavior by providing custom rendering implementations. Each implementation is provided with relevant information about the cell, header or footer and returns something your framework adapter can render e.g. JSX/Components/strings/etc. This will depend on which adapter you are using.
There are a couple of formatters available to you:
cell: Used for formatting cells.aggregatedCell: Used for formatting cells when aggregated.header: Used for formatting headers.footer: Used for formatting footers.
Cell Formatting
You can provide a custom cell formatter by passing a function to the cell property and using the props.getValue() function to access your cell's value:
columnHelper.accessor('firstName', {
cell: (props) => <span>{props.getValue().toUpperCase()}</span>,
})Cell formatters are also provided the row and table objects, so you can customize the cell formatting beyond just the cell value. The example below provides firstName as the accessor, but also displays a prefixed user ID located on the original row object:
columnHelper.accessor('firstName', {
cell: (props) => (
<span>{`${props.row.original.id} - ${props.getValue()}`}</span>
),
})Aggregated Cell Formatting
Aggregated cells render in place of regular cells when rows are grouped. This is discussed in more detail in the Grouping Guide, which is available for every framework adapter.
Header & Footer Formatting
Headers and footers do not have access to row data, but still use the same concepts for displaying custom content.
Feature Options on Column Defs
Beyond accessors and formatters, column defs carry per-column options for whichever features your table has enabled. Common examples include sortFn, filterFn, aggregationFn, and enable flags such as enableSorting or enableColumnFilter.
columnHelper.accessor('age', {
sortFn: 'basic', // a name typed from the sortFns registry in tableFeatures
filterFn: 'inNumberRange', // a name typed from the filterFns registry
sortDescFirst: true,
})In v9, the string names you can pass to these options are typed from the function registries you include in tableFeatures (such as sortFns, filterFns, and aggregationFns). Passing a name that is not in the registry is a type error. Custom functions can be provided inline or registered under your own names. See the Type Helpers Guide for how registries shape these types, and each feature guide for the full set of available options.
Column Meta
Column defs also accept a meta property for attaching your own arbitrary, strongly-typed data to a column (for example, a filter variant or a detected data type). See the Table and Column Meta Guide for details on typing it with metaHelper.
Guide Columns (`docs/guide/columns.md`)
title: Columns Guide
[!NOTE]
This guide is about the actualcolumnobjects that are generated within the table instance and NOT about setting up the column definitions for your table.
This quick guide will discuss the different ways you can retrieve and interact with column objects in TanStack Table.
Where to Get Columns From
You can find the column objects in many places. They are often attached to other objects like headers and cells, and they are also available from several table instance APIs.
Header and Cell Objects
Before you reach for one of the table instance APIs, consider if you actually need to retrieve either headers or cells instead of columns. If you are rendering out the markup for your table, you will most likely want to reach for the APIs that return headers or cells instead of columns. The column objects themselves are not really meant to render out the headers or cells, but the header and cell objects will contain references to these column objects from which they can derive the necessary information to render their UI.
const column = cell.column // get column from cell
const column = header.column // get column from headerColumn Table Instance APIs
There are dozens of table instance APIs you can use to retrieve columns from the table instance. Which APIs you will use will depend entirely on which features you are using in your table and your use-case.
Get Column
If you need to just get a single column by its ID, you can use the table.getColumn API.
const column = table.getColumn('firstName')Get Columns
The simplest column API is table.getAllColumns, which will return a list of all columns in the table. There are dozens of other column APIs that are affected by other features and the state of the table that come alongside this API though. table.getAllFlatColumns, table.getAllLeafColumns, table.getCenterLeafColumns, table.getStartVisibleLeafColumns are just some examples of other column APIs that you might use in tandem with the column visibility or column pinning features.
Column Objects
Column objects are not actually meant to be used to render out the table UI directly, so they are not associated 1-to-1 with any or elements in your table, but they contain a lot of useful properties and methods that you can use to interact with the table state.
Column IDs
Every column must have a unique id defined in its associated Column Definition. Usually, you define this id yourself, or it is derived from the accessorKey or header properties in the column definition.
ColumnDef
A reference to the original columnDef object that was used to create the column is always available on the column object.
Nested Grouped Columns Properties
There are a few properties on column objects that are only useful if the column is part of a nested or grouped column structure. These properties include:
columns: An array of child columns that belong to a group column.depth: The header group "row index" that the column group belongs to.parent: The parent column of the column. If the column is a top-level column, this will beundefined.
More Column APIs
There are dozens of Column APIs that you can use to interact with the table state and extract cell values from the table based on the state of the table. See each feature's column API documentation for more information.
Column Rendering
Don't necessarily use column objects to render headers or cells directly. Instead, use the header and cell objects, as discussed above.
But if you are just rendering a list of columns somewhere else in your UI for something like a column visibility menu or something similar, you can just map over a columns array and render out the UI as you normally would.
Guide Data (`docs/guide/data.md`)
title: Data Guide
Tables start with your data. Your column definitions and rows will depend on the shape of your data. TanStack Table has some TypeScript features that will help you create the rest of your table code with a great type-safe experience. If you set up your data and types correctly, TanStack Table will be able to infer the shape of your data and enforce that your column definitions are made correctly.
TypeScript
TypeScript is NOT required to use the TanStack Table packages... BUT TanStack Table is written and organized in such a way that makes the awesome TypeScript experience that you get feel like it is one of the main selling points of the library. If you are not using TypeScript, you will be missing out on a lot of great autocompletion and type-checking features that will both speed up your development time and reduce the number of bugs in your code.
TypeScript Generics
Having a basic understanding of what TypeScript Generics are and how they work will help you understand this guide better, but it should be easy enough to pick up as you go. The official TypeScript Generics Docs may be helpful for those not yet familiar with TypeScript.
Defining Data Types
data is an array of objects that will be turned into the r