import { createColumn, useDataTable } from '@uhg-abyss/web/hooks/useDataTable';import type { ColumnFiltersState, ColumnOrderState, ColumnPinningState, DataTableColumn, DataTableRowData, GlobalFilterState, PaginationState, RowSelectionState, SortingState,} from '@uhg-abyss/web/hooks/useDataTable';State types
Use the exported table state types to strongly type your useState hooks.
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);const [pagination, setPagination] = useState<PaginationState>({ pageIndex: 0, pageSize: 10,});const [rowSelection, setRowSelection] = useState<RowSelectionState>({});const [columnOrder, setColumnOrder] = useState<ColumnOrderState>([]);const [columnPinning, setColumnPinning] = useState<ColumnPinningState>({});const [sorting, setSorting] = useState<SortingState>([]);const [globalFilter, setGlobalFilter] = useState<GlobalFilterState>('');
const dataTableProps = useDataTable({ initialData: data, initialColumns: columns, tableConfig: { state: { columnFilters, pagination, rowSelection, columnOrder, columnPinning, sorting, globalFilter, }, onColumnFiltersChange: setColumnFilters, onPaginationChange: setPagination, onRowSelectionChange: setRowSelection, onColumnOrderChange: setColumnOrder, onColumnPinningChange: setColumnPinning, onSortingChange: setSorting, onGlobalFilterChange: setGlobalFilter, },});Typing columns
Teams looking for additional column type safety can use the DataTableColumn type and the createColumn function to define columns with full TypeScript support.
Using DataTableColumn type
Use DataTableColumn to type your columns array:
type Person = { uniqueId: string; firstName: string; lastName: string; age: number; visits: number; status: 'relationship' | 'complicated' | 'single';};
const columns: DataTableColumn<Person>[] = [ { header: 'First Name', accessorKey: 'firstName', cell: (info) => { const row = info.row.original; const existingData = row.lastName; // ✓ TypeScript knows this exists const nonExistentData = row.pizza; // ✗ TypeScript error - property doesn't exist return info.getValue(); }, footer: 'Footer 1', meta: { headerLabel: 'First Name Column', }, }, { header: 'Age', accessorKey: 'age', },];
const dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns,});Using createColumn helper
The createColumn function provides type inference for individual column definitions:
type Person = { uniqueId: string; firstName: string; lastName: string; age: number;};
const columns = [ createColumn<Person>({ header: 'First Name', accessorKey: 'firstName', // ✓ TypeScript validates this key exists in Person cell: (info) => { const value = info.getValue(); // ✓ Typed as string return value.toUpperCase(); }, }), createColumn<Person>({ header: 'Age', accessorKey: 'age', // ✓ TypeScript validates this key exists cell: (info) => { const value = info.getValue(); // ✓ Typed as number return `${value} years old`; }, }),];
const dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns,});Typed columns and rows
Use DataTableColumn and DataTableRowData to type your columns and data.
type Row = { uniqueId: string; col1: string; col2: string;};
const columns: DataTableColumn<Row>[] = [ { header: 'Column 1', accessorKey: 'col1', }, { header: 'Column 2', accessorKey: 'col2', },];
const dataTableProps = useDataTable<Row>({ initialData: data, initialColumns: columns,});Row identification and type safety
Default: uniqueId field
DataTable uses a uniqueId field by default. When your data type includes this field,rowIdKey is optional:
type Person = { uniqueId: string; // ✓ DataTable will use this automatically firstName: string; lastName: string;};
const dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns, // rowIdKey is optional - uniqueId will be used by default});Custom ID field: rowIdKey required
TypeScript enforces that rowIdKey must be provided when your data type lacks a uniqueId field. This compile-time type safety prevents runtime errors from missing row identifiers.
type Person = { applicationGuid: string; // Custom ID field firstName: string; lastName: string; // No uniqueId field};
// ❌ TypeScript error: rowIdKey is requiredconst dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns,});
// ✓ Correct - rowIdKey specifiedconst dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns, rowIdKey: 'applicationGuid',});Why this matters
Row identifiers are critical for DataTable features like row selection, editing, drag-and-drop, and state management. The type system ensures you never forget to specify how rows should be uniquely identified, catching configuration errors at compile-time instead of runtime.
Configuration types
The following examples show how to type all DataTable configuration options.
Action column configuration
import type { ActionColumnConfig, ActionDropdownItems,} from '@uhg-abyss/web/hooks/useDataTable';
type Person = { uniqueId: string; name: string; status: string };
// Type dropdown action itemsconst actionItems: ActionDropdownItems<Person>[] = [ { label: 'Edit', icon: <IconSymbol icon="edit" />, onClick: ({ row, modifyRow }) => { modifyRow(row, { status: 'editing' }); }, checkDisabled: (row) => row.original.status === 'locked', }, { label: 'Delete', icon: <IconSymbol icon="delete" />, onClick: ({ row, deleteRow }) => { deleteRow(row); }, isSeparated: true, },];
// Type action configconst actionConfig: ActionColumnConfig<Person> = { actionMode: 'dropdown', items: actionItems, dropdownConfig: { label: 'Actions', disableWhenAllItemsDisabled: true, },};
const dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns, actionColumnConfig: actionConfig,});Download dropdown menu items
import type { DownloadDropdownMenuItem } from '@uhg-abyss/web/ui/DataTable';
const downloadMenuItems: DownloadDropdownMenuItem[] = [ { title: 'Export All', onClick: 'exportAllData', icon: <IconSymbol icon="download" />, }, { title: 'Export Filtered', onClick: 'exportFilteredData', }, { title: 'Custom Export', onClick: (tableInstance) => { const data = tableInstance.getFilteredRowModel().rows; // Custom export logic with full type safety }, },];
<DataTable.DownloadDropdown dropdownMenuItems={downloadMenuItems} />;Column filter configuration
import type { ColumnFilterConfig } from '@uhg-abyss/web/hooks/useDataTable';
const filterConfig: ColumnFilterConfig = { defaultSettings: { filterMode: 'advanced', caseSensitive: false, textDefaultCondition: 'contains', dateDefaultCondition: 'equals', }, individualSettings: { firstName: { inputConfig: { type: 'text' }, defaultCondition: 'startsWith', caseSensitive: true, }, birthDate: { inputConfig: { type: 'date' }, defaultCondition: 'between', }, status: { inputConfig: { type: 'select', options: [ { value: 'active', label: 'Active' }, { value: 'inactive', label: 'Inactive' }, ], }, }, },};
const dataTableProps = useDataTable({ initialData: data, initialColumns: columns, columnFilterConfig: filterConfig,});Expand column configuration
import type { ExpandColumnConfig } from '@uhg-abyss/web/hooks/useDataTable';
type Person = { uniqueId: string; name: string; details: string };
const expandConfig: ExpandColumnConfig<Person> = { expandMode: 'subComponent', renderSubComponent: ({ row }) => ( <div> <h4>{row.original.name}</h4> <p>{row.original.details}</p> </div> ), subComponentHeight: 200,};
const dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns, expandColumnConfig: expandConfig,});Drag and drop configuration
import type { DragAndDropConfig } from '@uhg-abyss/web/hooks/useDataTable';
type Person = { uniqueId: string; name: string };
const dragDropConfig: DragAndDropConfig<Person> = { enableRowReorder: true, enableColumnReorder: true, onRowsReordered: (oldIndex, newIndex, prevData, updatedData) => { console.log('Rows reordered', { oldIndex, newIndex }); // Save new order to backend }, onColumnsReordered: (oldIndex, newIndex, prevOrder, updatedOrder) => { console.log('Columns reordered', { prevOrder, updatedOrder }); },};
const dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns, dragAndDropConfig: dragDropConfig,});Edit cell configuration
import type { EditCellConfig } from '@uhg-abyss/web/hooks/useDataTable';
type Person = { uniqueId: string; name: string; status: string };
const editConfig: EditCellConfig<Person> = { enableColumnEdit: true, canEditRow: (row) => row.status !== 'locked', onEditCompleted: (previousRow, updatedRow) => { console.log('Edit completed', { previousRow, updatedRow }); // Save changes to backend },};
const dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns, editCellConfig: editConfig,});Select column configuration
import type { SelectColumnConfig } from '@uhg-abyss/web/hooks/useDataTable';
type Person = { uniqueId: string; name: string };
const selectConfig: SelectColumnConfig<Person> = { selectionMode: 'multi',};
const dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns, selectColumnConfig: selectConfig,});Pagination configuration
import type { PaginationConfig } from '@uhg-abyss/web/hooks/useDataTable';
const paginationConfig: PaginationConfig = { enablePagination: true, pageSizeOptions: [10, 25, 50, 100],};
const dataTableProps = useDataTable({ initialData: data, initialColumns: columns, paginationConfig,});Complete example with all configs typed
import { useState } from 'react';import { useDataTable } from '@uhg-abyss/web/hooks/useDataTable';import type { ActionColumnConfig, ColumnFilterConfig, DataTableColumn, DefaultSettingsConfig, DragAndDropConfig, EditCellConfig, ExpandColumnConfig, PaginationConfig, PaginationState, RowSelectionState, SelectColumnConfig, SortingState,} from '@uhg-abyss/web/hooks/useDataTable';
type Person = { uniqueId: string; firstName: string; lastName: string; age: number; status: string;};
// Type all configurationsconst columns: DataTableColumn<Person>[] = [ { header: 'First Name', accessorKey: 'firstName' }, { header: 'Last Name', accessorKey: 'lastName' }, { header: 'Age', accessorKey: 'age' },];
const paginationConfig: PaginationConfig = { enablePagination: true, pageSizeOptions: [10, 25, 50],};
const columnFilterConfig: ColumnFilterConfig = { defaultSettings: { filterMode: 'advanced', caseSensitive: false, },};
const actionConfig: ActionColumnConfig<Person> = { actionMode: 'dropdown', items: [ { label: 'Edit', icon: <IconSymbol icon="edit" />, onClick: ({ row }) => console.log('Edit', row.original), }, ],};
const selectConfig: SelectColumnConfig<Person> = { selectionMode: 'multi',};
const defaultSettings: DefaultSettingsConfig = { rowHeight: 'comfortable', hideEmptyColumns: false,};
// Type all stateconst [pagination, setPagination] = useState<PaginationState>({ pageIndex: 0, pageSize: 25,});
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});const [sorting, setSorting] = useState<SortingState>([]);
// Everything is fully typedconst dataTableProps = useDataTable<Person>({ initialData: data, initialColumns: columns, paginationConfig, columnFilterConfig, actionColumnConfig: actionConfig, selectColumnConfig: selectConfig, defaultSettingsConfig: defaultSettings, tableConfig: { state: { pagination, rowSelection, sorting }, onPaginationChange: setPagination, onRowSelectionChange: setRowSelection, onSortingChange: setSorting, },});