# A shadcn table with TanStack Table

TanStack Table handles sorting and filters, the shadcn Table components draw the markup. Version 9 changed the API: useTable replaces useReactTable.

> Page: https://ui.eneawork.it/blog/shadcn-table-tanstack
> Markdown: https://ui.eneawork.it/blog/shadcn-table-tanstack.md

*2026-11-01*

Risposta breve: monta i componenti Table di shadcn e passa dati e colonne a TanStack Table, che gestisce ordinamento, filtri e paginazione senza generare markup. La tabella resta tua, la logica no.

@tanstack/react-table 9.3.1 dichiara 122.555 byte non compressi su npm, misurato il 1 novembre 2026, con licenza MIT. La versione 9 ha cambiato API: useTable sostituisce useReactTable e le feature si dichiarano in tableFeatures. L'API precedente resta in @tanstack/react-table/legacy, come useLegacyTable.

## Le colonne

import { createSortedRowModel, rowSortingFeature, sortFns, tableFeatures, } from "@tanstack/react-table"; import type { ColumnDef } from "@tanstack/react-table"; type Invoice = { id: string; total: number }; const features = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns, }); export const columns: Array> = [ { accessorKey: "id", header: "Fattura" }, { accessorKey: "total", header: "Totale", cell: (info) => info.getValue().toFixed(2) + " EUR", }, ];

Le feature sono opt-in: tableFeatures riceve solo ciò che usi, quindi ordinamento, filtri e paginazione non entrano nel bundle se non li dichiari. Il core row model c'è sempre, gli altri row model si registrano come slot.

## La tabella

import { useTable } from "@tanstack/react-table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { columns, features } from "./columns"; export function InvoiceTable({ data }: { data: Array }) { const table = useTable({ key: "invoices", features, columns, data }); return ( {table.getHeaderGroups().map((group) => ( {group.headers.map((header) => ( {header.isPlaceholder ? null : } ))} ))} {table.getRowModel().rows.map((row) => ( {row.getAllCells().map((cell) => ( ))} ))} ); }

Il metodo getToggleSortingHandler arriva dalla feature di ordinamento appena registrata: senza quella riga in tableFeatures il click non cambia nulla. FlexRender disegna la cella con la funzione che hai scritto nella colonna, oppure stampa il valore.

## Perché due livelli

Il vantaggio è la separazione: TanStack non sa cosa sia un tr, shadcn non sa cosa sia un ordinamento. Puoi cambiare il markup senza riscrivere la logica, o sostituire il markup con una lista su mobile, e i due pezzi continuano a funzionare.

## Il comando

npm install @tanstack/react-table@9 npx shadcn@latest add table

## Record vivi nel catalogo

Tre record mostrano il risultato: la libreria shadcn, il blocco table-01 con intestazioni e paginazione, e la categoria blocchi tabella. La licenza è indicata su ogni pagina.

## La limitazione

Il pacchetto non virtualizza: una tabella con mille righe monta mille nodi, e per alleggerirla serve TanStack Virtual, un altro pacchetto MIT. Inoltre l'ordinamento client-side lavora sui dati già scaricati; con diecimila righe la paginazione deve passare dal server, altrimenti il costo si sposta solo nella rete.

## Dove continuare

Il modello di distribuzione è in che cos'è shadcn/ui; l'errore più comune con flexRender è in TanStack Table e flexRender; il confronto fra librerie è in librerie di tabelle React.

## FAQ

### Perché useReactTable non esiste più?

Perché la versione 9 espone useTable con tableFeatures. L'API precedente resta disponibile dall'export legacy del pacchetto, con useLegacyTable al posto di useReactTable.

### TanStack Table genera la tabella?

No, è headless: restituisce stato e funzioni, il markup lo scrivi tu. Con shadcn usi i componenti Table, TableRow e TableCell, quindi lo stile resta quello del design system.

### Come aggiungo i filtri?

Registri la feature di filtering e il row model filtrato in tableFeatures, poi colleghi un input a column.setFilterValue. Senza la registrazione l'API di filtro non esiste sul tavolo.

### Funziona con dati caricati dal server?

Sì, in modalità manuale: passi le righe della pagina corrente e lasci che ordinamento e paginazione siano parametri della richiesta. Le funzioni getSortedRowModel e getPaginationRowModel non servono in quel caso.

### Meglio TanStack Table o i componenti Table di shadcn da soli?

I componenti Table bastano per una tabella statica di poche righe. TanStack Table serve quando arrivano ordinamento, filtri, selezione o paginazione, perché quella logica scritta a mano diventa presto uno stato duplicato.
