Skip to main content

DataTable - Types

Types and state helpers for DataTable.

github
View source code
import { createColumn, useDataTable } from '@uhg-abyss/web/hooks/useDataTable';
import type {
ColumnFiltersState,
ColumnVisibilityState,
DataTableColumn,
DataTableRowData,
ExpandedState,
GlobalFilterState,
GroupingState,
PaginationState,
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 [sorting, setSorting] = useState<SortingState>([]);
const [globalFilter, setGlobalFilter] = useState<GlobalFilterState>('');
const [columnVisibility, setColumnVisibility] = useState<ColumnVisibilityState>(
{}
);
const [expanded, setExpanded] = useState<ExpandedState>({});
const [grouping, setGrouping] = useState<GroupingState>([]);
const dataTableProps = useDataTable({
initialData: data,
initialColumns: columns,
tableConfig: {
state: {
columnFilters,
pagination,
sorting,
globalFilter,
columnVisibility,
expanded,
grouping,
},
onColumnFiltersChange: setColumnFilters,
onPaginationChange: setPagination,
onSortingChange: setSorting,
onGlobalFilterChange: setGlobalFilter,
onColumnVisibilityChange: setColumnVisibility,
onExpandedChange: setExpanded,
onGroupingChange: setGrouping,
},
});

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 required
const dataTableProps = useDataTable<Person>({
initialData: data,
initialColumns: columns,
});
// ✓ Correct - rowIdKey specified
const 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 items
const 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 config
const 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,
enableSingleRowEdit: 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,
SelectColumnConfig,
SortingState,
} from '@uhg-abyss/web/hooks/useDataTable';
type Person = {
uniqueid: string;
firstName: string;
lastName: string;
age: number;
status: string;
};
// Type all configurations
const 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 state
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 25,
});
const [sorting, setSorting] = useState<SortingState>([]);
// Everything is fully typed
const dataTableProps = useDataTable<Person>({
initialData: data,
initialColumns: columns,
paginationConfig,
columnFilterConfig,
actionColumnConfig: actionConfig,
selectColumnConfig: selectConfig,
defaultSettingsConfig: defaultSettings,
tableConfig: {
state: { pagination, sorting },
onPaginationChange: setPagination,
onSortingChange: setSorting,
},
});
Table of Contents