React

Tabella shadcn con TanStack Table

TanStack Table gestisce ordinamento e filtri, i componenti Table disegnano il markup. La versione 9 ha cambiato API: useTable sostituisce useReactTable.

In sintesi

  • TanStack Table è headless: non genera markup, quindi il markup resta dei componenti Table di shadcn.
  • Dalla 9.3.1 l'API è useTable con tableFeatures; useReactTable è sostituito da useLegacyTable.
  • Il pacchetto pesa 122.555 byte non compressi e ha licenza MIT.

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.

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

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.

Domande frequenti

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.