Filtering

Built-in per-column filter popups with operator selection, two-condition AND/OR logic, and support for text, number, and date column types.

Column filters — text, number, and date

Click the filter icon in any column header. Pick an operator, enter a value, then click Apply. Try salary ≥ 130000, or filter hired Before 2021-01-01.

Name
Department
Salary
Hired
Aria Chen
Engineering
$155,000
2019-03-12
Avery Johnson
Sales
$104,000
2021-08-14
Blake Turner
Product
$127,000
2019-11-25
Casey Park
Design
$109,000
2022-11-19
Dakota Silva
Engineering
$152,000
2019-07-08
Devon Wright
Engineering
$158,000
2016-12-01
Drew Santos
Product
$138,000
2020-03-30
Elliot Ramos
Design
$121,000
2021-12-13
Emerson Cole
Analytics
$136,000
2020-09-11
Finley Grant
Analytics
$147,000
2017-11-29

Filtering runs on the full dataset before pagination, so a filter matches rows on every page, not just the one you are viewing. The result count and page navigation update to the filtered set. This applies to client-side pagination; see theserver-side recipe for the server case.

Quick start

Add filterable: true and a stable id to any column. A filter icon appears in the column header. Filters across columns combine with AND. A row must pass every active filter to appear.

const columns: TableColumn<Row>[] = [{ id: 'name', name: 'Name', selector: r => r.name, filterable: true }];

<DataTable columns={columns} data={data} />;

Filter types

Set filterType to get the right operator set and input widget. Defaults to "text".

const columns: TableColumn<Row>[] = [
  { id: 'name', name: 'Name', selector: r => r.name, filterable: true },
  { id: 'score', name: 'Score', selector: r => r.score, filterable: true, filterType: 'number' },
  { id: 'dob', name: 'Birth date', selector: r => r.dob, filterable: true, filterType: 'date' },
  { id: 'seen', name: 'Last seen', selector: r => r.seen, filterable: true, filterType: 'datetime' },
  { id: 'ranAt', name: 'Ran at', selector: r => r.ranAt, filterable: true, filterType: 'time' },
];
filterTypeDefault operatorInputOperators
"text" (default)ContainsTextContains, Does not contain, Equals, Does not equal, Begins with, Ends with, Blank, Not blank
"number"EqualsNumberEquals, Does not equal, Greater than, ≥, Less than, ≤, Between, Blank, Not blank
"date"EqualsDateEquals, Before, After, Between, Blank, Not blank
"datetime"EqualsDate & timeEquals, Before, After, Between, Blank, Not blank
"time"EqualsTimeEquals, Before, After, Between, Blank, Not blank

Blank / Not blank match on empty cells and need no value input.Between (number, date, datetime, and time) shows two value inputs for inclusive bounds. For "date" and "datetime" columns, selector should return an ISO string ("2024-03-15" or "2024-03-15T14:30") or any value parseable by new Date().

"date" compares by calendar day, so Equals matches any time on that day."datetime" compares the exact instant, so Equals matches a specific minute. Becausedatetime-local inputs are timezone-naive, filtering is exact only when your cell values are also local time (no Z / offset); otherwise supply a filterFunction.

"time" compares the time of day and ignores the date, so it filters across every date at once — useful for logs (“anything after 17:00”, “errors between 02:00 and 04:00”). The input accepts seconds, and the cell value may be a bare time ("17:30") or any timestamp whose time portion is read ("2024-03-15T17:30:45"). A Between whose start is later than its end wraps past midnight, so 22:0006:00 matches an overnight window.

Time-of-day filter

Log rows across several days. Open the Time filter, choose Between, and enter 02:00 and 04:00 to surface the nightly cron failures regardless of date. Try 22:00 to 06:00 for an overnight window that wraps past midnight.

Time
Service
Level
Message
08:12:04
auth
info
session started
23:41:19
billing
warn
retry scheduled
02:15:00
cron
error
nightly job failed
13:05:47
api
info
request handled
02:47:31
cron
error
nightly job failed
09:30:12
auth
info
session started
17:58:00
billing
warn
card declined
03:22:09
cron
error
nightly job failed
21:14:55
api
info
request handled
00:38:41
auth
warn
rate limited

Two conditions per column

Each filter popup has a + Add condition link. Adding a second condition reveals an AND / OR toggle. AND means both conditions must match; OR means either must match.

import type { FilterState } from 'react-data-table-component';

// The shape of one column's filter state
const filter: FilterState = {
  condition1: { operator: 'startsWith', value: 'J' },
  condition2: { operator: 'endsWith', value: 'son' },
  logic: 'AND', // 'AND' | 'OR' — defaults to 'AND'
};

Apply / Clear behaviour

Filters apply only when the user clicks Apply. Typing does not immediately re-filter. This avoids jarring mid-keystroke changes on large datasets. Clicking Clear resets the column's filter and applies immediately.

Custom filter function

Override built-in operator logic per column with filterFunction. It receives the full FilterState so both conditions are available:

import type { TableColumn, FilterState } from 'react-data-table-component';

const columns: TableColumn<Row>[] = [
  {
    id: 'tags',
    name: 'Tags',
    selector: r => r.tags.join(', '),
    filterable: true,
    filterFunction: (row, filter) => {
      const term = (filter.condition1.value ?? '').toLowerCase();
      return row.tags.some(tag => tag.toLowerCase().includes(term));
    },
  },
];

Controlled mode

Pass filterValues and onFilterChange to own the filter state yourself. Useful for persisting it in a URL or resetting it programmatically.onFilterChange fires on every Apply or Clear click.

import { useState } from 'react';
import DataTable, { type FilterState } from 'react-data-table-component';

function App() {
  const [filterValues, setFilterValues] = useState<Record<string | number, FilterState>>({});
  const [resetPage, setResetPage] = useState(false);

  function handleFilterChange(columnId: string | number, filter: FilterState) {
    setFilterValues(prev => ({ ...prev, [columnId]: filter }));
    setResetPage(v => !v); // jump back to page 1 after each filter
  }

  return (
    <DataTable
      columns={columns}
      data={data}
      filterValues={filterValues}
      onFilterChange={handleFilterChange}
      pagination
      paginationResetDefaultPage={resetPage}
    />
  );
}

Utility exports

import { emptyFilterState, isFilterActive, type FilterState } from 'react-data-table-component';

// Create a default-empty FilterState for a given type
emptyFilterState('number'); // { condition1: { operator: 'equals' } }
emptyFilterState('text'); // { condition1: { operator: 'contains' } }

// Check whether a FilterState is actually filtering anything
isFilterActive({ condition1: { operator: 'contains' } }); // false — no value
isFilterActive({ condition1: { operator: 'contains', value: 'a' } }); // true
isFilterActive({ condition1: { operator: 'blank' } }); // true — no value needed

Localization

Use the localization prop to swap every string in the table UI. Import a pre-built locale or build your own — all keys are optional and fall back to English defaults.

// Drop-in locale
import DataTable from 'react-data-table-component';
import { fr } from 'react-data-table-component/locales';

<DataTable columns={columns} data={data} localization={fr} />;
// Custom / partial override — spread a locale and replace only what you need
import { fr } from 'react-data-table-component/locales';
import type { Localization } from 'react-data-table-component';

const myLocale: Localization = {
  ...fr,
  filter: {
    ...fr.filter,
    applyLabel: 'Valider', // override one key
  },
};

<DataTable columns={columns} data={data} localization={myLocale} />;
// Built from scratch — every key is optional
import type { Localization } from 'react-data-table-component';

const custom: Localization = {
  filter: {
    clearLabel: 'Reset',
    applyLabel: 'Go',
    operators: { contains: 'has', equals: 'is' },
  },
};

<DataTable columns={columns} data={data} localization={custom} />;

Headless usage

Use useColumnFilter directly when building a custom table with the headless hooks. See Headless hooks for the full API.

import { useColumnFilter, type FilterState } from 'react-data-table-component';

const { filterValues, handleFilterChange, filteredData } = useColumnFilter(columns);

// Call handleFilterChange when the user applies a filter in your custom UI
function onApply(columnId: string | number, filter: FilterState) {
  handleFilterChange(columnId, filter);
}

// Apply all active filters before rendering rows
const rows = filteredData(tableRows);

See it combined with other features in the Server-side sort, page & filter recipe and URL-synced table state.

Prop reference

PropTypeDefaultDescription
filterValuesRecord<string | number, FilterState>-Controlled filter state. Omit to use internal state. See Filtering.
onFilterChange(columnId, filter: FilterState) => void-Called when the user clicks Apply or Clear in a filter popup.

Per-column filtering is configured on each TableColumnvia filterable, filterType, and filterFunction.