Appearance
DataTable API
Complete type definitions and prop reference for the DataTable ecosystem.
DataTable
Enterprise-grade data table with automatic client/server mode detection.
Import
tsx
import { DataTable, type DataTableColumn, type DataTableProps, type TableMode, type TableDensity } from '@/shared/ui/components/table/DataTable'Props
DataTableProps<T>
| Prop | Type | Default | Description |
|---|---|---|---|
| Data & Columns | |||
columns | DataTableColumn<T>[] | Required | Column definitions |
data | T[] | Required | Array of data objects |
| Mode Configuration | |||
mode | 'client' | 'server' | Auto-detect | Table processing mode. Auto-detects based on callbacks |
| Loading & Error States | |||
isLoading | boolean | false | Show loading spinner |
isError | boolean | false | Show error state |
loadingContent | ReactNode | Default spinner | Custom loading content |
errorContent | ReactNode | Default error | Custom error content |
emptyContent | ReactNode | "No results found" | Custom empty state |
| Server-Side Callbacks | |||
onSortingChange | (sorting: SortingState) => void | - | Called when sorting changes (triggers server mode) |
onColumnFiltersChange | (filters: ColumnFiltersState) => void | - | Called when filters change (triggers server mode) |
onPaginationChange | (pagination: PaginationState) => void | - | Called when pagination changes (triggers server mode) |
onRowSelectionChange | (rows: T[]) => void | - | Called when row selection changes |
onGlobalFilterChange | (search: string) => void | - | Called when global search changes (server mode) |
| Server-Side Metadata | |||
totalCount | number | - | Total number of records (required for server-side pagination) |
| Feature Toggles | |||
enableRowSelection | boolean | false | Enable row selection checkboxes |
enableGlobalSearch | boolean | true | Enable global search input |
enableColumnVisibility | boolean | true | Enable column visibility toggle |
enableColumnResizing | boolean | true | Enable column resizing by dragging |
enableDensitySelector | boolean | true | Enable density selector (compact/normal/spacious) |
enableCsvExport | boolean | true | Enable CSV export button |
| Virtual Scrolling | |||
enableVirtualScroll | boolean | Auto (50+ rows) | Enable virtual scrolling for performance |
virtualScrollHeight | number | 600 | Max height in pixels for virtual scroll container |
virtualScrollOverscan | number | 10 | Number of rows to render outside viewport |
| Pagination | |||
pageSizeOptions | number[] | [10, 20, 50] | Available page size options |
defaultPageSize | number | 10 | Initial page size |
| Density | |||
density | 'compact' | 'normal' | 'spacious' | 'normal' | Row height density |
| State Persistence | |||
storageKey | string | - | localStorage key for persisting table state |
| Initial State | |||
initialState | TableInitialState | - | Initial table state (sorting, filters, pagination, visibility) |
| External Control | |||
selectedRows | T[] | - | Externally controlled selected rows (for clearing selection) |
| Row Actions | |||
rowActions | (row: T) => ReactNode | - | Render function for row action buttons |
pinActions | 'right' | 'none' | 'right' | Pin actions column to right side |
| Row Identification | |||
getRowId | (row: T, index: number) => string | - | Custom row ID function (important for selection) |
| Styling | |||
className | string | - | Additional CSS classes for container |
| Custom Export | |||
onExportCsv | () => void | - | Custom CSV export handler (overrides default) |
| Labels (i18n) | |||
labels | DataTableLabels | - | Custom labels for internationalization |
TableInitialState
ts
interface TableInitialState {
sorting?: SortingState
columnFilters?: ColumnFiltersState
pagination?: { pageIndex: number; pageSize: number }
columnVisibility?: VisibilityState
}DataTableLabels
ts
interface DataTableLabels {
loading?: string // Default: "Loading..."
noResults?: string // Default: "No results found"
errorMessage?: string // Default: "Failed to load data"
actions?: string // Default: "Actions"
clearFilters?: string // Default: "Clear Filters"
exportCsv?: string // Default: "Export CSV"
columns?: string // Default: "Columns"
search?: string // Default: "Search…"
selectFilter?: string // Default: "Select…"
}Column Definition
DataTableColumn<T>
| Property | Type | Description |
|---|---|---|
id | string | Required. Unique column identifier |
header | ReactNode | Required. Column header content (string or component) |
cell | (row: T) => ReactNode | Required. Cell render function |
accessorKey | string | Accessor key for sorting/filtering (defaults to id) |
sortable | boolean | Enable sorting for this column |
sortId | string | Server-side sort field name (defaults to id) |
filter | FilterConfig | Column filter configuration |
visible | boolean | Initial visibility (default: true) |
width | number | Initial width in pixels |
minWidth | number | Minimum width in pixels (default: 80) |
maxWidth | number | Maximum width in pixels |
className | string | Additional CSS classes for cells |
mobileRender | (row: T) => ReactNode | Custom render for mobile card view |
exportValue | (row: T) => unknown | Custom value for CSV export |
exportDefaultValue | unknown | Default value if exportValue returns null/undefined |
FilterConfig
ts
type FilterConfig =
| { type: 'text'; placeholder?: string }
| { type: 'select'; options: Array<{ label: string; value: string }> }Text Filter:
- Client mode: Fuzzy search (case-insensitive substring match)
- Server mode: Debounced search (300ms default)
Select Filter:
- Exact match filtering
- Dropdown with predefined options
Type Exports
ts
export type TableMode = 'client' | 'server'
export type TableDensity = 'compact' | 'normal' | 'spacious'
// TanStack Table re-exports
export type {
SortingState,
ColumnFiltersState,
VisibilityState,
PaginationState,
RowSelectionState,
} from '@tanstack/react-table'Example
tsx
import { DataTable, type DataTableColumn } from '@/shared/ui/components/table/DataTable'
type User = {
id: number
name: string
email: string
role: 'Admin' | 'Manager' | 'Member'
status: 'Active' | 'Inactive'
}
const columns: DataTableColumn<User>[] = [
{
id: 'name',
header: 'Full Name',
cell: (row) => <span className="font-medium">{row.name}</span>,
accessorKey: 'name',
sortable: true,
filter: {
type: 'text',
placeholder: 'Search names...'
},
width: 200,
minWidth: 150,
},
{
id: 'email',
header: 'Email Address',
cell: (row) => row.email,
sortable: true,
width: 250,
},
{
id: 'role',
header: 'Role',
cell: (row) => row.role,
filter: {
type: 'select',
options: [
{ label: 'Admin', value: 'Admin' },
{ label: 'Manager', value: 'Manager' },
{ label: 'Member', value: 'Member' },
]
},
},
{
id: 'status',
header: 'Status',
cell: (row) => (
<Badge variant={row.status === 'Active' ? 'default' : 'secondary'}>
{row.status}
</Badge>
),
filter: {
type: 'select',
options: [
{ label: 'Active', value: 'Active' },
{ label: 'Inactive', value: 'Inactive' },
]
},
},
]
function UsersTable() {
const [selected, setSelected] = useState<User[]>([])
return (
<DataTable
columns={columns}
data={users}
enableRowSelection
enableGlobalSearch
enableColumnVisibility
enableColumnResizing
enableDensitySelector
enableCsvExport
pageSizeOptions={[10, 25, 50]}
defaultPageSize={25}
storageKey="users-table-v1"
density="normal"
onRowSelectionChange={setSelected}
selectedRows={selected}
getRowId={(row) => String(row.id)}
/>
)
}SimpleSortableTable
Lightweight table component for simpler use cases.
Import
tsx
import { SimpleSortableTable } from '@/shared/ui/components/table/SimpleSortableTable'
import { type ColumnDef } from '@tanstack/react-table'Props
SimpleSortableTableProps<T>
| Prop | Type | Default | Description |
|---|---|---|---|
columns | ColumnDef<T>[] | Required | TanStack Table column definitions |
data | T[] | Required | Array of data objects |
initialSort | SortingState | [] | Initial sorting state |
density | 'compact' | 'normal' | 'spacious' | 'normal' | Row height density |
className | string | - | Additional CSS classes |
onRowClick | (row: T) => void | - | Row click handler |
selectedRowId | string | - | ID of currently selected row (for highlighting) |
getRowId | (row: T) => string | - | Custom row ID function |
expandable | boolean | false | Enable row expansion |
renderExpandedRow | (row: T, toggleExpand: () => void) => ReactNode | - | Render function for expanded content |
expandedRowIds | string[] | - | Controlled expanded row IDs |
onExpandedChange | (ids: string[]) => void | - | Callback when expanded rows change |
multiExpand | boolean | true | Allow multiple rows to be expanded |
defaultExpandedRowIds | string[] | [] | Initially expanded row IDs |
enablePagination | boolean | false | Enable pagination |
defaultPageSize | number | 10 | Default page size |
pageSizeOptions | number[] | [5, 10, 20, 50] | Page size options |
Example
tsx
import { SimpleSortableTable } from '@/shared/ui/components/table/SimpleSortableTable'
import { type ColumnDef } from '@tanstack/react-table'
type Task = {
id: string
title: string
priority: number
status: string
}
const columns: ColumnDef<Task>[] = [
{
accessorKey: 'title',
header: 'Task Title',
},
{
accessorKey: 'priority',
header: 'Priority',
enableSorting: true,
},
{
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => (
<Badge>{row.original.status}</Badge>
),
},
]
function TaskList({ tasks }: { tasks: Task[] }) {
return (
<SimpleSortableTable
columns={columns}
data={tasks}
density="normal"
onRowClick={(task) => navigate(`/tasks/${task.id}`)}
expandable
renderExpandedRow={(task) => (
<div className="p-4">
<p>Task details for: {task.title}</p>
</div>
)}
/>
)
}TablePagination
Standalone pagination component used by DataTable.
Import
tsx
import { TablePagination } from '@/shared/ui/components/table/TablePagination'Props
TablePaginationProps
| Prop | Type | Default | Description |
|---|---|---|---|
pageIndex | number | Required | Current page index (0-based) |
pageSize | number | Required | Current page size |
totalCount | number | - | Total number of items (optional for cursor-based) |
pageSizeOptions | number[] | [10, 20, 50, 100] | Available page size options |
onPageChange | (pageIndex: number) => void | - | Callback when page changes |
onPageSizeChange | (pageSize: number) => void | - | Callback when page size changes |
canPreviousPage | boolean | pageIndex > 0 | Disable previous button |
canNextPage | boolean | true | Disable next button |
labels | PaginationLabels | - | Custom labels for i18n |
className | string | - | Additional CSS classes |
PaginationLabels
ts
interface PaginationLabels {
rowsPerPage?: string // Default: "Rows per page"
page?: string // Default: "Page"
of?: string // Default: "of"
}Example
tsx
import { TablePagination } from '@/shared/ui/components/table/TablePagination'
function CustomPagination() {
const [pageIndex, setPageIndex] = useState(0)
const [pageSize, setPageSize] = useState(10)
return (
<TablePagination
pageIndex={pageIndex}
pageSize={pageSize}
totalCount={250}
pageSizeOptions={[10, 25, 50, 100]}
onPageChange={setPageIndex}
onPageSizeChange={(size) => {
setPageSize(size)
setPageIndex(0) // Reset to first page
}}
labels={{
rowsPerPage: 'Items per page',
page: 'Page',
of: 'of',
}}
/>
)
}EditableTableCell
Inline editable table cell component.
Import
tsx
import { EditableTableCell } from '@/shared/ui/components/table/EditableTableCell'Props
EditableTableCellProps
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | number | Required | Current value to display and edit |
onSave | (value: string | number) => void | Required | Callback when value is saved |
type | 'text' | 'number' | 'text' | Input type |
prefix | string | - | Prefix to display (e.g., "$" for currency) |
className | string | - | Additional CSS classes |
validate | (value: string | number) => boolean | - | Custom validation function |
Built-in Validation
- Text: Prevents saving empty strings
- Number: Prevents saving NaN or negative numbers
- Custom: Use
validateprop for additional rules
Example
tsx
import { EditableTableCell } from '@/shared/ui/components/table/EditableTableCell'
import { DataTable, type DataTableColumn } from '@/shared/ui/components/table/DataTable'
type Product = {
id: number
name: string
price: number
stock: number
}
const columns: DataTableColumn<Product>[] = [
{
id: 'name',
header: 'Product Name',
cell: (row) => (
<EditableTableCell
value={row.name}
type="text"
onSave={(newName) => updateProduct(row.id, { name: newName })}
validate={(value) => String(value).length >= 3}
/>
),
},
{
id: 'price',
header: 'Price',
cell: (row) => (
<EditableTableCell
value={row.price}
type="number"
prefix="$"
onSave={(newPrice) => updateProduct(row.id, { price: newPrice })}
validate={(value) => Number(value) > 0}
/>
),
},
{
id: 'stock',
header: 'Stock',
cell: (row) => (
<EditableTableCell
value={row.stock}
type="number"
onSave={(newStock) => updateProduct(row.id, { stock: newStock })}
/>
),
},
]
function ProductsTable({ products }: { products: Product[] }) {
return <DataTable columns={columns} data={products} />
}Type Reference
TanStack Table Types
The DataTable component re-exports commonly used TanStack Table types:
ts
import type {
SortingState,
ColumnFiltersState,
VisibilityState,
PaginationState,
RowSelectionState,
ColumnSizingState,
Row,
Table,
ColumnDef,
} from '@tanstack/react-table'Sorting State
ts
type SortingState = Array<{
id: string // Column ID
desc: boolean // true = descending, false = ascending
}>
// Example
const sorting: SortingState = [
{ id: 'name', desc: false }, // Sort by name ascending
{ id: 'createdAt', desc: true }, // Then by date descending
]Column Filters State
ts
type ColumnFiltersState = Array<{
id: string // Column ID
value: unknown // Filter value
}>
// Example
const filters: ColumnFiltersState = [
{ id: 'role', value: 'Admin' },
{ id: 'status', value: 'Active' },
]Pagination State
ts
type PaginationState = {
pageIndex: number // 0-based page index
pageSize: number // Number of rows per page
}
// Example
const pagination: PaginationState = {
pageIndex: 2, // Third page (0-indexed)
pageSize: 25, // 25 rows per page
}Row Selection State
ts
type RowSelectionState = Record<string, boolean>
// Example
const rowSelection: RowSelectionState = {
'1': true, // Row with ID '1' is selected
'5': true, // Row with ID '5' is selected
'10': true, // Row with ID '10' is selected
}