TanStack Table mit Claude Code: Hochperformante Datentabellen 2026
TanStack Table (früher React Table) ist die headless Table-Library für React — keine Styles, volle Kontrolle, vollständig typisiert. Claude Code kennt alle Features: Sorting, Filtering, Pagination, Row Selection, Column Pinning und Virtualisierung für Millionen von Rows.
Setup und erste Tabelle
SetupGrundlegende Tabelle mit Typen
# npm install @tanstack/react-table
// Prompt: "Erstelle eine typisierte Datentabelle für User-Management"
import {
useReactTable, getCoreRowModel, getSortedRowModel,
getFilteredRowModel, getPaginationRowModel,
createColumnHelper, flexRender,
ColumnDef, SortingState, ColumnFiltersState,
} from '@tanstack/react-table';
// Typ definieren:
type User = {
id: string;
name: string;
email: string;
role: 'admin' | 'user';
createdAt: Date;
active: boolean;
};
// Column Helper für vollständige Typsicherheit:
const columnHelper = createColumnHelper<User>();
const columns = [
columnHelper.display({
id: 'select',
header: ({ table }) => (
<input type="checkbox"
checked={table.getIsAllPageRowsSelected()}
onChange={table.getToggleAllPageRowsSelectedHandler()}
/>
),
cell: ({ row }) => (
<input type="checkbox"
checked={row.getIsSelected()}
onChange={row.getToggleSelectedHandler()}
/>
),
}),
columnHelper.accessor('name', {
header: 'Name',
cell: info => <strong>{info.getValue()}</strong>,
enableSorting: true,
enableColumnFilter: true,
}),
columnHelper.accessor('email', {
header: 'E-Mail',
enableSorting: true,
}),
columnHelper.accessor('role', {
header: 'Rolle',
cell: info => (
<span className={`badge ${info.getValue()}`}>{info.getValue()}</span>
),
filterFn: 'equals',
}),
columnHelper.accessor('createdAt', {
header: 'Erstellt am',
cell: info => info.getValue().toLocaleDateString('de-DE'),
sortingFn: 'datetime',
}),
columnHelper.accessor('active', {
header: 'Status',
cell: info => info.getValue() ? '✅ Aktiv' : '❌ Inaktiv',
filterFn: 'equals',
}),
];
Sorting, Filtering und Pagination
TabelleVollständige User-Tabelle mit allen Features
function UserTable({ data }: { data: User[] }) {
const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [globalFilter, setGlobalFilter] = useState('');
const [rowSelection, setRowSelection] = useState({});
const table = useReactTable({
data,
columns,
state: { sorting, columnFilters, globalFilter, rowSelection },
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onGlobalFilterChange: setGlobalFilter,
onRowSelectionChange: setRowSelection,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
enableRowSelection: true,
initialState: { pagination: { pageSize: 20 } },
});
return (
<div>
{/* Globale Suche */}
<input
value={globalFilter}
onChange={e => setGlobalFilter(e.target.value)}
placeholder="Alle Spalten durchsuchen..."
/>
{/* Tabelle */}
<table>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => (
<th key={header.id}
onClick={header.column.getToggleSortingHandler()}
style={{ cursor: header.column.getCanSort() ? 'pointer' : 'default' }}
>
{flexRender(header.column.columnDef.header, header.getContext())}
{header.column.getIsSorted() === 'asc' ? ' ↑' : ''}
{header.column.getIsSorted() === 'desc' ? ' ↓' : ''}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => (
<tr key={row.id} data-selected={row.getIsSelected()}>
{row.getVisibleCells().map(cell => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
{/* Pagination */}
<div>
<button onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>←</button>
<span>Seite {table.getState().pagination.pageIndex + 1} von {table.getPageCount()}</span>
<button onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>→</button>
<select value={table.getState().pagination.pageSize}
onChange={e => table.setPageSize(Number(e.target.value))}>
{[10, 20, 50, 100].map(size => <option key={size}>{size}</option>)}
</select>
</div>
</div>
);
}
Server-Side Sorting und Filtering
Server-SideFür große Datensätze: API-basiert
// Prompt: "TanStack Table mit Server-Side Sorting/Filtering über API"
function ServerTable() {
const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 20 });
// React Query / SWR für Data Fetching:
const { data, isLoading } = useQuery({
queryKey: ['users', sorting, columnFilters, pagination],
queryFn: () => fetchUsers({
sortBy: sorting[0]?.id,
sortDir: sorting[0]?.desc ? 'desc' : 'asc',
filters: columnFilters,
page: pagination.pageIndex,
pageSize: pagination.pageSize,
}),
placeholderData: keepPreviousData, // Verhindert Flackern beim Paginieren
});
const table = useReactTable({
data: data?.rows ?? [],
columns,
state: { sorting, columnFilters, pagination },
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onPaginationChange: setPagination,
getCoreRowModel: getCoreRowModel(),
manualSorting: true, // Server übernimmt Sorting
manualFiltering: true, // Server übernimmt Filtering
manualPagination: true, // Server übernimmt Pagination
pageCount: data?.pageCount ?? -1,
});
if (isLoading) return <TableSkeleton />;
// ... render table
}
Client vs. Server-Side: Client-Side für <10.000 Rows (alle Daten im Browser). Server-Side für größere Datensätze — API übernimmt Sorting, Filtering, Pagination. Claude Code erkennt die Datenmenge und wählt automatisch den richtigen Ansatz.
Virtualisierung: Millionen von Rows
VirtualisierungTanStack Virtual + Table für Mega-Tabellen
// npm install @tanstack/react-virtual
// Prompt: "Virtualisierte Tabelle für 100.000+ Rows"
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualTable({ data }: { data: User[] }) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
});
const { rows } = table.getRowModel();
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 48, // Geschätzte Row-Höhe in px
overscan: 10, // Puffer außerhalb des Viewports
});
return (
<div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize() + 'px', position: 'relative' }}>
{virtualizer.getVirtualItems().map(virtualRow => {
const row = rows[virtualRow.index];
return (
<tr key={row.id} style={{
position: 'absolute',
top: virtualRow.start + 'px',
height: virtualRow.size + 'px',
}}>
{row.getVisibleCells().map(cell => (
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
))}
</tr>
);
})}
</div>
</div>
);
}
// 100.000 Rows: nur ~10-20 DOM-Nodes gleichzeitig — 60fps garantiert!
Datentabellen-Modul im Kurs
Im Claude Code Mastery Kurs: vollständiges TanStack Table Modul mit Client-Side und Server-Side Tabellen, Row Selection, Column Pinning, Virtualisierung und Export-Funktionen — inkl. shadcn/ui Integration.
14 Tage kostenlos testen →