react-table
React Table Implementation
This project uses @tanstack/react-table v8 and renders with the project’s Table primitives in @/modules/common/components/ui/table (shadcn-based).
Architecture overview
| Layer | Location | Responsibility |
|---|---|---|
| Column config | constants/*TableConfig.tsx |
ColumnDef<T>[]: header, accessor, cell, filterFn, size, etc. |
| Container | containers/*Container.tsx |
useReactTable, data source, optional virtual/selection/filter |
| Common component | common/components/ui/data-table.tsx |
Simple tables can use <DataTable columns={} data={} /> |
| Table UI | common/components/ui/table |
Table, TableHeader, TableBody, TableRow / SelectableTableRow, TableHead, TableCell |
1. Column configuration
- Type:
ColumnDef<TData>[]from@tanstack/react-table. - Common properties:
id: unique identifier (optional; otherwise derived from accessor)header: string or() => ReactNode(use a function when composing UI like sort buttons)accessorKeyoraccessorFn: value accessorcell:({ row }) => ReactNode, typically viarow.getValue<T>(key)orrow.originalsize: number (px). Used for fixed widths and virtualized layoutsfilterFn: custom filter function (see below)enableSorting,sortingFn: sorting controls
Example (simplified):
// constants/exampleTableConfig.tsx
import type { ColumnDef } from '@tanstack/react-table';
export const exampleTableColumns: ColumnDef<ExampleRow>[] = [
{ header: 'Name', accessorKey: 'name', cell: ({ row }) => row.getValue('name') },
{
id: 'amount',
header: 'Amount',
accessorFn: (row) => row.amount,
cell: ({ row }) => <span>{row.original.amount}</span>,
size: 120,
},
];
2. Creating a table instance
In a container, call useReactTable with at least data, columns, and getCoreRowModel: getCoreRowModel().
import {
flexRender,
getCoreRowModel,
useReactTable,
} from '@tanstack/react-table';
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
Add advanced features only when needed:
- Sorting:
getSortedRowModel: getSortedRowModel() - Filtering:
getFilteredRowModel: getFilteredRowModel(), optionallyfilterFns: {}(or a map from filter name to fn) - Row selection:
enableRowSelection: true,enableMultiRowSelection: true/false, usingrow.toggleSelected()andtable.getSelectedRowModel().rows
3. Rendering
Render headers and cells using the project Table primitives and flexRender.
Header:
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id} style={{ width: header.getSize() }}>
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
Body:
<TableBody>
{table.getRowModel().rows.map((row) => (
<TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))}
</TableBody>
For empty states, render a single <TableRow> with <TableCell colSpan={columns.length}>No results.</TableCell>.
4. Optional patterns
Simple tables: use DataTable
If you don’t need row selection, virtualization, or custom filters:
import { DataTable } from '@/modules/common/components/ui/data-table';
<DataTable columns={columns} data={data} />
Row selection
- Enable selection with
enableRowSelection: trueand setenableMultiRowSelectiontofalse(single) ortrue(multi). - Use
SelectableTableRow; callrow.toggleSelected()on click; style selected state viadata-state={row.getIsSelected() && 'selected'}. - “Select all”: render a checkbox in the header using
table.getIsAllRowsSelected()andtable.getToggleAllRowsSelectedHandler(). - Get selected rows via
table.getSelectedRowModel().rows.map((row) => row.original).
Virtualized body
- Use the project
useStableVirtualizer(built on@tanstack/react-virtual), providingcount: rows.length, a stablegetScrollElement(outer scroll container ref),estimateSize, optionalmeasureElement, andoverscan. - Use the same scroll container ref for
getScrollElement. - Only render
rowVirtualizer.getVirtualItems(). Position rows withtransform: translateY(${virtualRow.start}px)and set the body container height torowVirtualizer.getTotalSize(). - When data (or
rows.length) changes, akeyor state bump may be used to force recalculation (seeDiscoverTableContainer/PublicChannelContainer).
Custom filtering (filterFn)
- Filter function signature:
(row, columnId, filterValue, addMeta) => boolean(seeRowfrom@tanstack/react-table). - Set
filterFn: customizedXxxFilteron the column definition (e.g.customizedTimeFilter,customizedAddressFilter). - Update filters from the container via
table.getColumn(columnId)?.setFilterValue(value)(often driven by filter UI such as time/address dialogs). - Keep reusable filter implementations in
utils/tableFilters.ts, then import them into column configs.
Sorting
- Add
getSortedRowModel: getSortedRowModel(). - When a header needs sorting UI, use the project
SortButtonwithonSort={() => column.toggleSorting()}andsortDirection={column.getIsSorted()}. - Columns can set
enableSorting: true,sortingFn: 'auto', or a customsortingFn.
5. File & type conventions
- Column configs: live under
constants/<feature>TableConfig.tsx. ExportColumnDef<T>[]or a factory likecreateXxxTableConfig(...). Export row types asXxxRow/XxxItem. - Containers: fetch/derive data (react-query, atoms, etc.), build
data+columns, calluseReactTable, then render with Table primitives +flexRender. UseuseRefto hold the table instance or scroll container for virtualization/filter wiring. - Table primitives: use
SelectableTableRowfor selectable rows; otherwiseTableRow. For virtualization,TableBodyoften usesdisplay: gridandposition: relative, while rows useposition: absolute+translateY.
6. Checklist (when adding/modifying a table)
- Column configs live in
constants/*TableConfig.tsxand are typed asColumnDef<T>[] - The container calls
useReactTablewith at leastdata,columns, andgetCoreRowModel() - Rendering uses
Table/TableHeader/TableBody/TableRow(orSelectableTableRow) andTableHead/TableCellwithflexRender - Sorting: add
getSortedRowModel()and header UI (projectSortButton) where needed - Filtering: add
getFilteredRowModel(), setfilterFnon columns, and update values viagetColumn(id)?.setFilterValue(...) - Large datasets: consider virtualization (
useStableVirtualizer+ a stable scroll container) - Selection: use
SelectableTableRow+row.toggleSelected()and read selection viagetSelectedRowModel()
How to Use
Read individual reference files for detailed code and patterns:
references/README.md
references/minimal-data-table.md
references/row-selection.md
references/sorting-virtualized.md
references/filtering.md
references/table-filters.md
references/multi-select-header.md
references/table-primitives.md
Each reference file contains focused documentation and code examples for that topic. When implementing or modifying tables, load these as needed instead of main-project paths.