A thin wrapper around TanStack's `useReactTable` with sensible defaults for server-driven (Relay/REST) pagination, sorting, and filtering. Row-model factories are instantiated once at module load to preserve internal TanStack caches across renders. ## Key Components - **`UseDataTableOptions`** — Extends TanStack's `TableOptions` (omitting `getCoreRowModel`) with two additional flags: - `clientSideSorting` — enables `getSortedRowModel` (default: `false`) - `clientSideFiltering` — enables `getFilteredRowModel` (default: `false`) - **`useDataTable`** — The main hook. Wraps `useReactTable` and automatically sets `manualSorting`/`manualFiltering` to the inverse of the client-side flags unless explicitly overridden. ## Usage Example ```typescript import { useMemo, useCallback } from 'react' import { useDataTable } from './use-data-table' import type { ColumnDef } from '@tanstack/react-table' // Stable references — required to avoid row-model rebuilds on every render const data = useMemo(() => rows.map(toUiRow), [rows]) const columns = useMemo[]>(() => [ { accessorKey: 'name', header: 'Name' }, { accessorKey: 'status', header: 'Status' }, ], []) const getRowId = useCallback((row: MyRow) => row.id, []) const sorting = useMemo(() => [{ id: sortKey, desc: sortDesc }], [sortKey, sortDesc]) const table = useDataTable({ data, columns, getRowId, state: { sorting }, // wrapper object can be inline onSortingChange: handleSort, // callbacks can be inline // clientSideSorting: true, // opt-in for fully client-side tables }) ``` ## Reference-Stability Notes | Option | Safe inline? | |---|---| | `data`, `columns` | ❌ — must `useMemo` | | `state` (wrapper) | ✅ — build inline freely | | `state.sorting`, `state.pagination` | ❌ — must be stable refs | | `onXChange` callbacks | ✅ — invoked, never compared | | `meta`, `defaultColumn` | ⚠️ — recommended `useMemo` |