← All posts

TanStack Table v9: pay only for the table features you register

Stable v9 four years after v8: tree-shakable features, TanStack Store, up to 86% less memory on a million rows, and ten framework adapters.

TanStack Table v9: pay only for the table features you register
Contents

In brief

React Status #486 leads with stable TanStack Table v9 — more than four years after v8. Kevin Van Cott (maintainer since Tanner Linsley handed over in 2022) rebuilt the core: you register features explicitly and unused ones tree-shake away; state sits on TanStack Store; in the team’s million-row benchmark retained heap drops from about 2.71 GB to ~380 MB. The headless table model you already know is still there; what changes is how you assemble a product-wide table system. Also in the issue: a Vercel guide to Next.js 16.3 and a worm in the keyv package — surrounding news, not the table story.

What happened

The canonical write-up is the TanStack blog (4 August 2026). The beta two months earlier promised mostly an internal frame. During beta that frame grew; the stable list has seven blocks.

Adapters: ten frameworks plus @tanstack/table-core. TanStack Store state plugs into native reactivity — signals, refs, runes, computed(). React 18+ works with the React Compiler and offers fine-grained reads via atoms, selectors, and table.Subscribe. Also shipping: Preact, Vue 3.2+, Solid 1.x (Solid 2 aimed at v10), Svelte 5, Angular 19+, Lit, Alpine, Ember 5.8+, and Octane.

Memory: shared prototypes for row, column, cell, and header APIs instead of copying methods onto every instance. On one million rows × 8 columns, paginated, retained heap in the report is about 86% lower than v8. Average processing time: core row model −79%, grouping −52%, sorting −37%, filtering −34%.

You can still drive state with state / on[State]Change or hand slices to external atoms. Types know which features you registered: filter APIs do not exist in the type until you opt in. Features compose through tableFeatures(): a pagination-only table does not ship filtering or grouping. The full package grew from ~14 KB to ~25 KB compressed, but the average app bundle should shrink. For a product-wide system: tableOptions() and createTableHook() — a factory with features and cell conventions already bound.

New capabilities include cell selection (rectangles, drag, Shift, disjoint ranges), cell spanning, and multiple aggregations per column. React gets a temporary useLegacyTable bridge with the v8-shaped API on v9 internals; it is deprecated, registers everything, and inflates the bundle — migration only. A pagination-only screen in the official example registers rowPaginationFeature and createPaginatedRowModel() — nothing else. That is the v9 promise in practice: the average app bundle should shrink even though the full package grew from ~14 KB to ~25 KB.

Why it matters

Products rarely have one table. You have orders, logs, admin grids — and in v8 the bundle often paid for “everything the library can do.” Explicit feature registration changes the economics: a grouping column is paid for by the screen that needs it. The maintainer handoff explains the long wait; the architecture is built so v10 (pivoting, richer filter expressions, Solid 2) does not force every app to carry the whole roadmap.

For frontend teams it is also a modularity lesson: “headless” in 2026 means not only “you draw the markup” but “you bundle only the row-model algorithms you actually run on the client.” If a grid virtualizes hundreds of thousands of rows, the memory win is not a micro-optimization — it is the difference between a tab that lives and a tab the browser kills. The team’s numbers are about that edge: tiny tables barely move, a million rows save hundreds of megabytes to gigabytes.

In practice

  1. New screens should start on v9: useTable plus explicit features, not a copy-paste of useReactTable from old guides.
  2. Migrate with your framework’s guide; treat useLegacyTable as a short bridge, not the target API.
  3. Register only what you use: client pagination is rowPaginationFeature and createPaginatedRowModel() — no “just in case” filters.
  4. Subscribe with table.Subscribe / selectors instead of re-rendering the whole grid on any state tick.
  5. If you have many tables, extract a shared createTableHook() with defaults, sort fns, and cells so individual screens stay short.
  6. Re-test large client datasets (hundreds of thousands of rows with virtualization): memory is the practical reason to take the major.
  7. Digest background: skim Vercel’s Next.js 16.3 efficiency guide on its own; the keyv worm is a lockfile check, not part of the table release.

Takeaway

TanStack Table v9 is a foundation, not a pile of extra header buttons. You pay for registered features, keep far less memory on huge client grids, get reactivity that matches the host framework, and ten adapters. The upgrade breaks hook names and feature registration — in exchange the library can grow without inflating every table in the app.