Composants · Référence
Data Table
A dependency-free table engine powered by one Effect Atom, with an AG-style API, controlled state, selection, grouping, gallery view, and Pointer Events interactions.
Dernière mise à jour le 19 octobre 2026
Overview
DataTable is a typed React table with search, sorting, pagination, selection, actions, grouping, gallery view, export, column sizing, and row reordering.
Set the active locale once with KrakstackProvider; pass locale directly only when a component needs an override. Message overrides are keyed by arbitrary locale strings, so additional languages can be supplied with messages={{ es: { ... } }}. Exact locale keys such as es-MX take precedence over base keys such as es; English is the final fallback.
Its public state is QueryType, so decoded TanStack Router search state can be shared directly with the table and an Effect Atom request. Column visibility, sizing, and view preferences remain internal and persist with Atom.kvs.
Install
bunx shadcn@latest add @krak-stack/data-tableUsage
<DataTable
columnDefs={columns}
features={{
search: true,
pagination: { mode: "client" },
selection: { onSelectionChange: setSelectedAssets },
}}
getRowId={(asset) => asset.id}
rowData={assets}
/>Use field for a row property, or combine colId with valueGetter for a derived value. cellRenderer replaces the built-in cell UI. Supply stable row IDs when using selection, grouping, or reordering.
Types
Columns without type render their value as text. Built-in column types add formatting, sorting, search, export, and editing behavior:
number— localized decimal, percentage, or currency formatting. Values are left-aligned and sorted numerically.boolean— a checkbox cell. AddonChangefor controlled editing; otherwise it remains read-only.date— localized calendar dates, includingYYYY-MM-DDvalues that should not shift across timezones.dateTime— localized instants fromDate, epoch milliseconds, or ISO timestamps with an explicit timezone.list— labels or avatar-style icons for collections and editable relationships.
Invalid or missing typed values use emptyLabel. Number, boolean, and date types support null ordering and raw or formatted export. valueFormatter, cellRenderer, and comparator override built-in behavior.
List columns use display: "list" for comma-separated labels or display: "icon" for images, custom icons, or initials. visibleCount controls the passive +N summary. Overflow and item actions open from the full-cell chevron menu.
Provide getOptions, manageLabel, and onAdd and/or onRemove to make a list editable. “Add item…” opens a searchable submenu; Remove appears under each selected item. Both operations keep the shared menu open. The caller owns persistence and row-data reconciliation.
Column Example
const columns = [
{
field: "name",
headerName: labels.name,
width: 240,
},
{
field: "amount",
headerName: labels.amount,
type: "number",
typeOptions: { style: "currency", currency: "CAD" },
},
{
field: "enabled",
headerName: labels.enabled,
type: "boolean",
typeOptions: {
getAriaLabel: (row) => labels.toggleEnabled(row.name),
onChange: ({ value, row }) => updateEnabled(row.id, value),
},
},
{
field: "createdAt",
headerName: labels.createdAt,
type: "dateTime",
typeOptions: { timeZone: "America/Toronto" },
},
{
field: "members",
headerName: labels.members,
type: "list",
typeOptions: {
display: "list",
emptyLabel: labels.noMembers,
manageLabel: labels.manageMembers,
getItems: (row) => row.members,
getOptions: () => availableMembers,
onAdd: ({ value, row }) => addMember(row.id, value),
onRemove: ({ value, row }) => removeMember(row.id, value),
},
},
] satisfies DataTableColDef<Organization>[];Actions Example
const rowActions = [
{
name: labels.edit,
icon: <Pencil />,
onClick: (row) => openEditor(row.id),
},
{
name: labels.archive,
icon: <Archive />,
variant: "destructive",
visible: (row) => row.status !== "archived",
onClick: (row) => archive(row.id),
},
] satisfies DataTableRowAction<Asset>[];
<DataTable
columnDefs={columns}
features={{ rowActions: { items: rowActions } }}
getRowId={(row) => row.id}
rowData={assets}
/>List itemActions use the same action shape and receive { item, row }. Table-level row actions remain in the sticky actions column. Confirm destructive or sensitive operations in the action handler.
State
Pass decoded QueryStandard route search to state, then navigate with onStateChange. Use initialState when route search only supplies mount-time defaults.
<DataTable
columnDefs={columns}
rowData={rows}
state={Route.useSearch()}
onStateChange={(next) =>
void navigate({ replace: true, search: next })
}
/>Client pagination processes rowData locally. Server pagination requires rowCount; fetching and page reconciliation remain caller-owned.
Export
Enable built-in CSV and JSON downloads with features={{ export: { baseName: "assets" } }}. Existing configurations need no changes. The default scope is currentPage; filteredRows exports all matching rows already supplied in rowData, not additional server pages. Both formats export visible columns and respect typed column export formatting.
App-Provided Export Actions
Use optional actions for full-result server exports or other application-owned formats. Each action has a stable id, localized label, optional icon and disabled, and an onExport: () => void | Promise<void> callback. Return the promise so the table can show busy state and prevent overlapping exports. No requests are made automatically.
<DataTable
columnDefs={columns}
rowData={page.data}
features={{
pagination: { mode: "server", rowCount: page.meta.total },
export: {
baseName: "assets",
builtIn: false,
actions: [{
id: "all-filtered-csv",
label: labels.exportAllFiltered,
onExport: () => exportAllMatching(search),
}],
onError: (error, action) => reportExportError(error, action.id),
},
}}
/>exportAllMatching and reportExportError are app-owned functions in this example. The app supplies its current filters and sorting, enforces server authorization, selects permitted fields, applies result limits, and downloads the response. The shared component does not infer API endpoints, fetch pages, or broaden data access.
builtIn defaults to true, adding actions alongside CSV/JSON. Set it false to offer only app actions. Enabled app actions remain available even when the current page is empty. While an action runs, the export menu is disabled and marked aria-busy. Rejections and synchronous failures show a generic English/French alert, invoke optional onError(error, action), and allow retry. Override the alert with locale-keyed messages.exportFailed. Custom errors are not displayed verbatim. Export progress and errors are transient and are not saved to local storage.
For non-table views, downloadCsv(headers, rows, fileName) and downloadJson(data, fileName) remain available.
Interaction Notes
- Selection and row operations should use stable IDs.
- Grouping, gallery view, and reordering share the same row model.
- Move up and Move down actions provide keyboard-accessible reordering.
- Application-provided labels remain caller-owned; built-in labels can be selected with
localeand extended through locale-keyedmessages.