TypeScript
Every public API is fully typed. In most cases you don't need to import any types at all —
column shapes and event handlers infer from the row type you pass to DataTable.
This page covers the patterns where explicit typing is useful.
Typed columns
Annotate your column array with TableColumn<T> so that
selector, cell, format, and conditionalCellStyles
all receive the correct row type:
import DataTable, { type TableColumn } from 'react-data-table-component';
interface Employee {
id: number;
name: string;
salary: number;
status: 'Active' | 'On Leave' | 'Terminated';
}
const columns: TableColumn<Employee>[] = [
{ name: 'Name', selector: row => row.name }, // row: Employee
{ name: 'Salary', selector: row => row.salary,
format: (row, idx) => `$${row.salary.toLocaleString()}` },
{ name: 'Status', selector: row => row.status, // typed as the union, not string
conditionalCellStyles: [
{ when: r => r.status === 'Terminated', style: { color: 'red' } },
],
},
];
If you ever see any appearing in your editor, double-check that the columns
array has an explicit TableColumn<Employee>[] annotation — TypeScript
cannot infer the row type from data alone when columns are declared separately.
Imperative ref
The ref attached to DataTable uses DataTableHandle:
import { useRef } from 'react';
import DataTable, { type DataTableHandle } from 'react-data-table-component';
export default function App() {
const tableRef = useRef<DataTableHandle>(null);
return (
<>
<button onClick={() => tableRef.current?.clearSelectedRows()}>
Clear selection
</button>
<DataTable ref={tableRef} selectableRows columns={columns} data={data} />
</>
);
} Sort callback
onSort receives a typed SortOrder enum value. Import it as a
value (not a type) since you'll compare against it:
import DataTable, { SortOrder, type TableColumn } from 'react-data-table-component';
function handleSort(column: TableColumn<Employee>, direction: SortOrder) {
if (direction === SortOrder.ASC) {
// ...
}
}
<DataTable columns={columns} data={data} sortServer onSort={handleSort} /> Conditional row styles
import { type ConditionalStyles } from 'react-data-table-component';
const conditionalRowStyles: ConditionalStyles<Employee>[] = [
{ when: row => row.salary > 150000, style: { backgroundColor: '#fefce8' } },
{ when: row => row.status === 'Terminated', style: { opacity: 0.5 } },
];
<DataTable columns={columns} data={data} conditionalRowStyles={conditionalRowStyles} /> Custom expander component
import { type ExpanderComponentProps } from 'react-data-table-component';
function EmployeeDetail({ data: row }: ExpanderComponentProps<Employee>) {
return (
<div style={{ padding: 12 }}>
<strong>{row.name}</strong> — ID {row.id}
</div>
);
}
<DataTable expandableRows expandableRowsComponent={EmployeeDetail} columns={columns} data={data} /> Filter values
For controlled column filtering, filterValues is keyed by column id with
FilterState values:
import { useState } from 'react';
import DataTable, { type FilterState, type FilterCondition } from 'react-data-table-component';
const [filters, setFilters] = useState<Record<string, FilterState>>({});
<DataTable
columns={columns}
data={data}
filterValues={filters}
onFilterChange={(columnId, next) => setFilters(prev => ({ ...prev, [columnId]: next }))}
/> Headless hooks
Each hook re-exports the types it accepts and returns. Most users won't need to import them — destructured returns are fully typed. If you wrap a hook in another, import the option types:
import { useTableState, useColumns, useColumnVisibility } from 'react-data-table-component';
import type { UseColumnVisibilityResult } from 'react-data-table-component'; Generics on the default export
DataTable is generic. When you pass columns: TableColumn<Employee>[]
the row type is inferred. To be explicit:
<DataTable<Employee>
columns={columns}
data={data}
onRowClicked={(row, e) => {
// row is typed as Employee
}}
/> Common type-related issues
Cell renderer return type errors
cell and selector return ReactNode. Returning a raw
object will fail. Stringify or wrap in JSX:
// ❌ Type error — object is not ReactNode
{ name: 'Tags', selector: row => row.tags } // row.tags: Tag[]
// ✅ Either format or render
{ name: 'Tags', selector: row => row.tags.join(', ') }
{ name: 'Tags', cell: row => <TagList tags={row.tags} /> } Column id type
id is string | number. If you key offsets or filter values off
column ids in your own code, cast at the boundary:
const handleCellEdit = (row: Employee, value: string, column: TableColumn<Employee>) => {
const field = column.id as keyof Employee; // narrow string | number → known field
setData(prev => prev.map(r => r.id === row.id ? { ...r, [field]: value } : r));
}; Imported types — full list
import type {
// Core
TableProps,
TableColumn,
TableRow,
DataTableHandle,
// Behaviors
ConditionalStyles,
Selector,
SortFunction,
ColumnGroup,
// Filtering
FilterType,
FilterOperator,
FilterCondition,
FilterState,
// Theming
TableStyles,
Theme,
ThemeProp,
Themes,
ColorMode,
ThemeIcons,
// Pagination
PaginationComponentProps,
PaginationIcons,
PaginationOptions,
PaginationServerOptions,
// Expanding
ExpanderComponentProps,
} from 'react-data-table-component';
import { SortOrder } from 'react-data-table-component'; // value import, not type