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