Skip to content

Column filter

Filter data by values or by a set of conditions, using Handsontable’s intuitive user interface or flexible API.

Overview

Filtering lets you quickly find the information that you’re looking for, based on specific criteria. This makes data analysis easier and faster, especially with large data sets.

Handsontable’s built-in filtering interface resembles Excel’s, so it’s intuitive even to non-technical users. And if you want to implement your own interface, you can easily filter data programmatically, using Handsontable’s API.

You can filter data by value, or use the built-in conditions, which are different for each of the available column types.

Filtering demo

Click on one of the column menu buttons (▼) and play around with filtering by selecting values or conditions-based criteria.

After filtering, the column readjusts its width to the longest value displayed on screen. To disable this behavior, set fixed column widths.

JavaScript
// to import filtering as an individual module, see the 'Import the filtering module' section of this page
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
// to import filtering as an individual module, see the 'Import the filtering module' section of this page
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

Enable filtering

To enable the filtering interface for all columns, you need to do two things:

  1. Set the filters option to true.
  2. Enable the interface by setting the dropdownMenu option to true.

Enabling the filters option without the interface can be useful if you plan to create your own custom interface for filtering by using the API.

<HotTable
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
/>

By default, the column menu presents the filtering interface along with other default items such as Insert column left. To display only the filtering interface, pass an array of filter items in the configuration.

JavaScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu, but display only the filter menu items
dropdownMenu={['filter_by_condition', 'filter_by_value', 'filter_action_bar']}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu, but display only the filter menu items
dropdownMenu={['filter_by_condition', 'filter_by_value', 'filter_action_bar']}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

Enable filtering for individual columns

You have control over which columns are filterable and for which columns the column menu is enabled. In the following demo, only the Brand column is filterable, while the other columns are not. However, the Model column still has the column menu available in case you want to have some useful items in the menu such as Clear column.

JavaScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
// remove the column menu button from the 'Brand', 'Price', and 'Date' columns
const removeColumnMenuButton = (col, TH) => {
if (col > 1) {
const button = TH.querySelector('.changeType');
if (!button) {
return;
}
button.parentElement.removeChild(button);
}
};
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering for all columns
filters={true}
// enable the column menu for all columns
// but display only the 'Filter by value' list and the 'OK' and 'Cancel' buttons
dropdownMenu={{
items: {
filter_by_value: {
// hide the 'Filter by value' list from all columns but the first one
hidden() {
return this.getSelectedRangeLast().to.col > 0;
},
},
filter_action_bar: {
// hide the 'OK' and 'Cancel' buttons from all columns but the first one
hidden() {
return this.getSelectedRangeLast().to.col > 0;
},
},
clear_column: {
// hide the 'Clear column' menu item from the first column
hidden() {
return this.getSelectedRangeLast().to.col < 1;
},
},
},
}}
// `afterGetColHeader()` is a Handsontable hook
// it's fired after Handsontable appends information about a column header to the table header
afterGetColHeader={removeColumnMenuButton}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
// remove the column menu button from the 'Brand', 'Price', and 'Date' columns
const removeColumnMenuButton = (col: number, TH: { querySelector: (value: string) => any }) => {
if (col > 1) {
const button = TH.querySelector('.changeType');
if (!button) {
return;
}
button.parentElement.removeChild(button);
}
};
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering for all columns
filters={true}
// enable the column menu for all columns
// but display only the 'Filter by value' list and the 'OK' and 'Cancel' buttons
dropdownMenu={{
items: {
filter_by_value: {
// hide the 'Filter by value' list from all columns but the first one
hidden() {
return this.getSelectedRangeLast()!.to.col > 0;
},
},
filter_action_bar: {
// hide the 'OK' and 'Cancel' buttons from all columns but the first one
hidden() {
return this.getSelectedRangeLast()!.to.col > 0;
},
},
clear_column: {
// hide the 'Clear column' menu item from the first column
hidden() {
return this.getSelectedRangeLast()!.to.col < 1;
},
},
},
}}
// `afterGetColHeader()` is a Handsontable hook
// it's fired after Handsontable appends information about a column header to the table header
afterGetColHeader={removeColumnMenuButton}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

Enable filtering within already filtered results

To apply filters based on the search input, set searchMode to 'apply'. You can then apply the filter by either pressing the Enter key while the search input is focused or by clicking the OK button.

<HotTable
// enable filtering
filters={{
searchMode: 'apply'
}}
// enable the column menu
dropdownMenu={true}
/>
JavaScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering with option
filters={{
searchMode: 'apply',
}}
dropdownMenu={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering with option
filters={{
searchMode: 'apply',
}}
dropdownMenu={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

Filter different types of data

With its built-in cell types, Handsontable makes it easy to handle common data types like text, numbers, and dates by providing numerous configuration options. In addition, the filtering feature is designed to understand the underlying data and provides an adaptive interface that is tailored to each data type.

JavaScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
model: 'Racing Socks',
size: 'S',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
color: 'Black',
},
{
model: 'HL Mountain Shirt',
size: 'XS',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
color: 'White',
},
{
model: 'Cycling Cap',
size: 'L',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
color: 'Green',
},
{
model: 'Ski Jacket',
size: 'M',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
color: 'Blue',
},
{
model: 'HL Goggles',
size: 'XL',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
color: 'Black',
},
]}
columns={[
{
title: 'Model',
// set the type of the 'Model' column
type: 'text',
data: 'model',
},
{
title: 'Price',
// set the type of the 'Price' column
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Sold on',
// set the type of the 'Date' column
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
// set the type of the 'Time' column
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
// set the type of the 'In stock' column
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
{
title: 'Size',
// set the type of the 'Size' column
type: 'dropdown',
data: 'size',
source: ['XS', 'S', 'M', 'L', 'XL'],
className: 'htCenter',
},
{
title: 'Color',
// set the type of the 'Size' column
type: 'autocomplete',
data: 'color',
source: ['White', 'Black', 'Yellow', 'Blue', 'Green'],
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height={175}
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
model: 'Racing Socks',
size: 'S',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
color: 'Black',
},
{
model: 'HL Mountain Shirt',
size: 'XS',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
color: 'White',
},
{
model: 'Cycling Cap',
size: 'L',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
color: 'Green',
},
{
model: 'Ski Jacket',
size: 'M',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
color: 'Blue',
},
{
model: 'HL Goggles',
size: 'XL',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
color: 'Black',
},
]}
columns={[
{
title: 'Model',
// set the type of the 'Model' column
type: 'text', // 'text' is the default type, so you can omit this line
data: 'model',
},
{
title: 'Price',
// set the type of the 'Price' column
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Sold on',
// set the type of the 'Date' column
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
// set the type of the 'Time' column
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
// set the type of the 'In stock' column
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
{
title: 'Size',
// set the type of the 'Size' column
type: 'dropdown',
data: 'size',
source: ['XS', 'S', 'M', 'L', 'XL'],
className: 'htCenter',
},
{
title: 'Color',
// set the type of the 'Size' column
type: 'autocomplete',
data: 'color',
source: ['White', 'Black', 'Yellow', 'Blue', 'Green'],
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height={175}
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

The following table contains all available filter operators for each built-in data type.

Cell typeAvailable filter operators
All cell typesDefault operators:

None
Is empty
Is not empty
Is equal to
Is not equal to
text
time
checkbox
dropdown
autocomplete
password
Default operators plus:

Begins with
Ends with
Contains
Does not contain
numericDefault operators plus:

Greater than
Greater than or equal to
Less than
Less than or equal to
Is between
Is not between
dateDefault operators plus:

Before (exclusive — boundary date excluded)
Before or equal to (boundary date included)
After (exclusive — boundary date excluded)
After or equal to (boundary date included)
Is between
Tomorrow
Today
Yesterday
intl-dateDefault operators plus:

Before (exclusive — boundary date excluded)
Before or equal to (boundary date included)
After (exclusive — boundary date excluded)
After or equal to (boundary date included)
Is between
Tomorrow
Today
Yesterday
intl-timeDefault operators plus:

Begins with
Ends with
Contains
Does not contain
Before (exclusive — boundary time excluded)
Before or equal to (boundary time included)
After (exclusive — boundary time excluded)
After or equal to (boundary time included)
Is between

The None operator clears the column’s filter. Its programmatic equivalent is filters.removeConditions(column). For more on clearing filters with the API, see Clear a column filter.

For the Before, After, Before or equal to, After or equal to, and Is between operators on date, intl-date, and intl-time columns, the filter menu shows a native date or time input. Pick the value from the browser’s picker or type it in your locale’s format. When you set the condition through the API instead, pass the value as an ISO 8601 string (YYYY-MM-DD for dates, HH:mm for times).

Filter data on initialization

You can filter data on Handsontable’s initialization. This lets you apply pre-defined filters every time you launch your grid.

To do this, use the API provided by the Filters plugin. For instance, the demo below demonstrates how you can start with a pre-applied filter to display only items priced less than $200.

JavaScript
// you need `useRef` to call Handsontable's instance methods
import { useRef, useEffect } from 'react';
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotTableComponentRef = useRef(null);
useEffect(() => {
const handsontableInstance = hotTableComponentRef.current?.hotInstance;
// get the `Filters` plugin, so you can use its API
const filters = handsontableInstance?.getPlugin('filters');
// filter data by the 'Price' column (column at index 2)
// to display only items that are less than ('lt') $200
filters?.addCondition(2, 'lt', [200]);
filters?.filter();
}, []);
return (
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
// you need `useRef` to call Handsontable's instance methods
import { useRef, useEffect } from 'react';
import { HotTable, HotTableRef } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotTableComponentRef = useRef<HotTableRef>(null);
useEffect(() => {
const handsontableInstance = hotTableComponentRef.current?.hotInstance;
// get the `Filters` plugin, so you can use its API
const filters = handsontableInstance?.getPlugin('filters');
// filter data by the 'Price' column (column at index 2)
// to display only items that are less than ('lt') $200
filters?.addCondition(2, 'lt', [200]);
filters?.filter();
}, []);
return (
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

External quick filter

Handsontable’s quick filter feature lets you search for a particular phrase in a specific column. To accomplish this, use methods filters.addCondition() and filters.filter().

JavaScript
import { useEffect, useRef, useState } from 'react';
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
// Embedded so the example is self-contained in bundler environments.
const QUICK_FILTER_STYLES = `
.controlsQuickFilter {
position: relative;
display: flex;
align-items: center;
gap: 0.75rem;
margin: 0;
padding: 0;
}
#filterField {
border: 1px solid var(--sl-color-gray-5, #e0e0e0);
background: none;
color: var(--sl-color-text, #333333);
font-size: var(--sl-text-sm, 0.875rem);
padding: 0.4rem 0.625rem;
outline: none;
width: 140px;
}
#filterField::placeholder { color: var(--sl-color-gray-3, #777777); }
#filterField:focus { border-color: var(--sl-color-accent, #1A42E8); }
#filterField:focus-visible {
border-color: var(--sl-color-accent, #1A42E8);
box-shadow: 0 0 0 1px var(--sl-color-accent, #1A42E8);
}
.filter-dropdown {
position: relative;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.filter-dropdown-label {
color: var(--sl-color-gray-2, #555555);
font-size: var(--sl-text-sm, 0.875rem);
white-space: nowrap;
}
.filter-dropdown-trigger {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: none;
border: 1px solid var(--sl-color-gray-5, #e0e0e0);
color: var(--sl-color-gray-2, #555555);
cursor: pointer;
font-size: var(--sl-text-sm, 0.875rem);
font-weight: 500;
padding: 0.4rem 0.625rem;
transition: color 0.15s, background-color 0.15s;
white-space: nowrap;
border-radius: 0;
}
.filter-dropdown-trigger:hover {
color: var(--sl-color-white, #333333);
background: var(--sl-color-gray-7, var(--sl-color-gray-6, #eeeeee));
}
.filter-dropdown-chevron {
flex-shrink: 0;
margin-inline-start: 0.15rem;
transition: transform 0.15s;
}
.filter-dropdown-trigger[aria-expanded='true'] .filter-dropdown-chevron { transform: rotate(180deg); }
.filter-dropdown-menu {
background: var(--sl-color-bg-nav, #ffffff);
border: 1px solid var(--sl-color-gray-5, #e0e0e0);
border-radius: 0;
box-shadow: none;
inset-inline-start: 0;
list-style: none;
margin: 0;
min-width: 100%;
overflow-y: auto;
padding: 0;
position: absolute;
top: 100%;
z-index: 9999;
}
.filter-dropdown-menu li {
align-items: center;
color: var(--sl-color-text, #333333);
display: flex;
font-size: var(--sl-text-sm, 0.875rem);
padding: 0.5rem 0.75rem;
cursor: pointer;
border-bottom: 1px solid var(--sl-color-gray-5, #e0e0e0);
transition: background 0.1s, color 0.1s;
white-space: nowrap;
list-style: none;
margin: 0;
}
.filter-dropdown-menu li:last-child { border-bottom: none; }
.filter-dropdown-menu li:hover,
.filter-dropdown-menu li:focus-visible {
background: var(--sl-color-gray-6, #eeeeee);
color: var(--sl-color-white, #333333);
outline: none;
}
.filter-dropdown-menu li[aria-selected='true'] {
color: var(--sl-color-white, #333333);
box-shadow: inset 0 0 0 1px var(--sl-color-accent, #1A42E8);
}
`;
const columns = [
{ value: '0', label: 'Brand' },
{ value: '1', label: 'Model' },
{ value: '2', label: 'Price' },
{ value: '3', label: 'Date' },
{ value: '4', label: 'Time' },
{ value: '5', label: 'In stock' },
];
const ExampleComponent = () => {
const hotTableComponentRef = useRef(null);
const dropdownRef = useRef(null);
const [selectedColumn, setSelectedColumn] = useState('0');
const [open, setOpen] = useState(false);
useEffect(() => {
const handleClickOutside = (e) => {
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
setOpen(false);
}
};
document.addEventListener('click', handleClickOutside);
return () => document.removeEventListener('click', handleClickOutside);
}, []);
const handleFilter = (event) => {
const handsontableInstance = hotTableComponentRef.current?.hotInstance;
const filtersPlugin = handsontableInstance?.getPlugin('filters');
filtersPlugin?.removeConditions(Number(selectedColumn));
filtersPlugin?.addCondition(Number(selectedColumn), 'contains', [event.target.value]);
filtersPlugin?.filter();
handsontableInstance?.render();
};
const handleSelect = (col) => {
setSelectedColumn(col.value);
setOpen(false);
};
const selectedLabel = columns.find((c) => c.value === selectedColumn)?.label || 'Brand';
return (
<>
<style>{QUICK_FILTER_STYLES}</style>
<div className="example-controls-container">
<div className="controlsQuickFilter">
<div className="filter-dropdown" ref={dropdownRef}>
<span className="filter-dropdown-label">Select a column:</span>
<button
className="filter-dropdown-trigger"
type="button"
aria-haspopup="listbox"
aria-expanded={open}
onClick={() => setOpen(!open)}
>
<span className="filter-dropdown-text">{selectedLabel}</span>
<svg className="filter-dropdown-chevron" aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 9l6 6l6 -6"/></svg>
</button>
{open && (
<ul className="filter-dropdown-menu" role="listbox">
{columns.map((col) => (
<li
key={col.value}
role="option"
aria-selected={col.value === selectedColumn}
onClick={() => handleSelect(col)}
>
{col.label}
</li>
))}
</ul>
)}
</div>
<input id="filterField" type="text" placeholder="Filter" onKeyUp={handleFilter} />
</div>
</div>
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
filters={true}
height="auto"
className="exampleQuickFilter"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
</>
);
};
export default ExampleComponent;
TypeScript
import { useEffect, useRef, useState } from 'react';
import { HotTable, HotTableRef } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
// Embedded so the example is self-contained in bundler environments.
const QUICK_FILTER_STYLES = `
.controlsQuickFilter {
position: relative;
display: flex;
align-items: center;
gap: 0.75rem;
margin: 0;
padding: 0;
}
#filterField {
border: 1px solid var(--sl-color-gray-5, #e0e0e0);
background: none;
color: var(--sl-color-text, #333333);
font-size: var(--sl-text-sm, 0.875rem);
padding: 0.4rem 0.625rem;
outline: none;
width: 140px;
}
#filterField::placeholder { color: var(--sl-color-gray-3, #777777); }
#filterField:focus { border-color: var(--sl-color-accent, #1A42E8); }
#filterField:focus-visible {
border-color: var(--sl-color-accent, #1A42E8);
box-shadow: 0 0 0 1px var(--sl-color-accent, #1A42E8);
}
.filter-dropdown {
position: relative;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.filter-dropdown-label {
color: var(--sl-color-gray-2, #555555);
font-size: var(--sl-text-sm, 0.875rem);
white-space: nowrap;
}
.filter-dropdown-trigger {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: none;
border: 1px solid var(--sl-color-gray-5, #e0e0e0);
color: var(--sl-color-gray-2, #555555);
cursor: pointer;
font-size: var(--sl-text-sm, 0.875rem);
font-weight: 500;
padding: 0.4rem 0.625rem;
transition: color 0.15s, background-color 0.15s;
white-space: nowrap;
border-radius: 0;
}
.filter-dropdown-trigger:hover {
color: var(--sl-color-white, #333333);
background: var(--sl-color-gray-7, var(--sl-color-gray-6, #eeeeee));
}
.filter-dropdown-chevron {
flex-shrink: 0;
margin-inline-start: 0.15rem;
transition: transform 0.15s;
}
.filter-dropdown-trigger[aria-expanded='true'] .filter-dropdown-chevron { transform: rotate(180deg); }
.filter-dropdown-menu {
background: var(--sl-color-bg-nav, #ffffff);
border: 1px solid var(--sl-color-gray-5, #e0e0e0);
border-radius: 0;
box-shadow: none;
inset-inline-start: 0;
list-style: none;
margin: 0;
min-width: 100%;
overflow-y: auto;
padding: 0;
position: absolute;
top: 100%;
z-index: 9999;
}
.filter-dropdown-menu li {
align-items: center;
color: var(--sl-color-text, #333333);
display: flex;
font-size: var(--sl-text-sm, 0.875rem);
padding: 0.5rem 0.75rem;
cursor: pointer;
border-bottom: 1px solid var(--sl-color-gray-5, #e0e0e0);
transition: background 0.1s, color 0.1s;
white-space: nowrap;
list-style: none;
margin: 0;
}
.filter-dropdown-menu li:last-child { border-bottom: none; }
.filter-dropdown-menu li:hover,
.filter-dropdown-menu li:focus-visible {
background: var(--sl-color-gray-6, #eeeeee);
color: var(--sl-color-white, #333333);
outline: none;
}
.filter-dropdown-menu li[aria-selected='true'] {
color: var(--sl-color-white, #333333);
box-shadow: inset 0 0 0 1px var(--sl-color-accent, #1A42E8);
}
`;
const columnOptions = [
{ value: '0', label: 'Brand' },
{ value: '1', label: 'Model' },
{ value: '2', label: 'Price' },
{ value: '3', label: 'Date' },
{ value: '4', label: 'Time' },
{ value: '5', label: 'In stock' },
];
const ExampleComponent = () => {
const hotTableComponentRef = useRef<HotTableRef>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
const [selectedColumn, setSelectedColumn] = useState('0');
const [open, setOpen] = useState(false);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
setOpen(false);
}
};
document.addEventListener('click', handleClickOutside);
return () => document.removeEventListener('click', handleClickOutside);
}, []);
const handleFilter = (event: React.KeyboardEvent<HTMLInputElement>) => {
const handsontableInstance = hotTableComponentRef.current?.hotInstance;
const filtersPlugin = handsontableInstance?.getPlugin('filters');
filtersPlugin?.removeConditions(Number(selectedColumn));
filtersPlugin?.addCondition(Number(selectedColumn), 'contains', [(event.target as HTMLInputElement).value]);
filtersPlugin?.filter();
handsontableInstance?.render();
};
const handleSelect = (col: { value: string; label: string }) => {
setSelectedColumn(col.value);
setOpen(false);
};
const selectedLabel = columnOptions.find((c) => c.value === selectedColumn)?.label || 'Brand';
return (
<>
<style>{QUICK_FILTER_STYLES}</style>
<div className="example-controls-container">
<div className="controlsQuickFilter">
<div className="filter-dropdown" ref={dropdownRef}>
<span className="filter-dropdown-label">Select a column:</span>
<button
className="filter-dropdown-trigger"
type="button"
aria-haspopup="listbox"
aria-expanded={open}
onClick={() => setOpen(!open)}
>
<span className="filter-dropdown-text">{selectedLabel}</span>
<svg className="filter-dropdown-chevron" aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 9l6 6l6 -6"/></svg>
</button>
{open && (
<ul className="filter-dropdown-menu" role="listbox">
{columnOptions.map((col) => (
<li
key={col.value}
role="option"
aria-selected={col.value === selectedColumn}
onClick={() => handleSelect(col)}
>
{col.label}
</li>
))}
</ul>
)}
</div>
<input id="filterField" type="text" placeholder="Filter" onKeyUp={handleFilter} />
</div>
</div>
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
filters={true}
height="auto"
className="exampleQuickFilter"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
</>
);
};
export default ExampleComponent;
CSS
.controlsQuickFilter {
position: relative;
display: flex;
align-items: center;
gap: 0.75rem;
margin: 0;
padding: 0;
}
#filterField {
border: 1px solid var(--sl-color-gray-5, #e0e0e0);
background: none;
color: var(--sl-color-text, #333333);
font-size: var(--sl-text-sm, 0.875rem);
padding: 0.4rem 0.625rem;
outline: none;
width: 140px;
}
#filterField::placeholder {
color: var(--sl-color-gray-3, #777777);
}
#filterField:focus {
border-color: var(--sl-color-accent, #1A42E8);
}
#filterField:focus-visible {
border-color: var(--sl-color-accent, #1A42E8);
box-shadow: 0 0 0 1px var(--sl-color-accent, #1A42E8);
}
/* Filter dropdown */
.filter-dropdown {
position: relative;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.filter-dropdown-label {
color: var(--sl-color-gray-2, #555555);
font-size: var(--sl-text-sm, 0.875rem);
white-space: nowrap;
}
.filter-dropdown-trigger {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: none;
border: 1px solid var(--sl-color-gray-5, #e0e0e0);
color: var(--sl-color-gray-2, #555555);
cursor: pointer;
font-size: var(--sl-text-sm, 0.875rem);
font-weight: 500;
padding: 0.4rem 0.625rem;
transition: color 0.15s, background-color 0.15s;
white-space: nowrap;
border-radius: 0;
}
.filter-dropdown-trigger:hover {
color: var(--sl-color-white, #333333);
background: var(--sl-color-gray-7, var(--sl-color-gray-6, #eeeeee));
}
.filter-dropdown-chevron {
flex-shrink: 0;
margin-inline-start: 0.15rem;
transition: transform 0.15s;
}
.filter-dropdown-trigger[aria-expanded='true'] .filter-dropdown-chevron {
transform: rotate(180deg);
}
.filter-dropdown-menu {
background: var(--sl-color-bg-nav, #ffffff);
border: 1px solid var(--sl-color-gray-5, #e0e0e0);
border-radius: 0;
box-shadow: none;
inset-inline-start: 0;
list-style: none;
margin: 0;
min-width: 100%;
overflow-y: auto;
padding: 0;
position: absolute;
top: 100%;
z-index: 9999;
}
.filter-dropdown-menu[hidden] {
display: none !important;
}
.filter-dropdown-menu li {
align-items: center;
color: var(--sl-color-text, #333333);
display: flex;
font-size: var(--sl-text-sm, 0.875rem);
padding: 0.5rem 0.75rem;
cursor: pointer;
border-bottom: 1px solid var(--sl-color-gray-5, #e0e0e0);
transition: background 0.1s, color 0.1s;
white-space: nowrap;
list-style: none;
margin: 0;
}
.filter-dropdown-menu li:last-child {
border-bottom: none;
}
.filter-dropdown-menu li:hover,
.filter-dropdown-menu li:focus-visible {
background: var(--sl-color-gray-6, #eeeeee);
color: var(--sl-color-white, #333333);
outline: none;
}
.filter-dropdown-menu li[aria-selected='true'] {
color: var(--sl-color-white, #333333);
box-shadow: inset 0 0 0 1px var(--sl-color-accent, #1A42E8);
}

Customize the filter button

The default button that opens the column menu can be styled with CSS by modifying button.changeType variables and its ::before pseudoclass that contains svg mask-image displaying an arrow down icon.

JavaScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
// to differentiate this example's CSS from other examples on this page
className="customFilterButtonExample1"
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
// to differentiate this example's CSS from other examples on this page
className="customFilterButtonExample1"
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
CSS
.handsontable.customFilterButtonExample1 .changeType {
--ht-icon-button-background-color: #e2e2e263;
--ht-icon-button-border-radius: 100%;
}
.handsontable.customFilterButtonExample1 .changeType::before {
-webkit-mask-image: url("data:image/svg+xml,%3Csvg width=%2716%27 height=%2716%27 viewBox=%270 0 16 16%27 fill=%27none%27 xmlns=%27http://www.w3.org/2000/svg%27%3E%3Cpath d=%27M6.83337 7.99984C6.83337 8.30926 6.95629 8.606 7.17508 8.8248C7.39388 9.04359 7.69062 9.1665 8.00004 9.1665C8.30946 9.1665 8.60621 9.04359 8.825 8.8248C9.04379 8.606 9.16671 8.30926 9.16671 7.99984C9.16671 7.69042 9.04379 7.39367 8.825 7.17488C8.60621 6.95609 8.30946 6.83317 8.00004 6.83317C7.69062 6.83317 7.39388 6.95609 7.17508 7.17488C6.95629 7.39367 6.83337 7.69042 6.83337 7.99984Z%27 fill=%27%23222222%27/%3E%3Cpath d=%27M6.83337 12.4165C6.83337 12.7259 6.95629 13.0227 7.17508 13.2415C7.39388 13.4603 7.69062 13.5832 8.00004 13.5832C8.30946 13.5832 8.60621 13.4603 8.825 13.2415C9.04379 13.0227 9.16671 12.7259 9.16671 12.4165C9.16671 12.1071 9.04379 11.8103 8.825 11.5915C8.60621 11.3728 8.30946 11.2498 8.00004 11.2498C7.69062 11.2498 7.39388 11.3728 7.17508 11.5915C6.95629 11.8103 6.83337 12.1071 6.83337 12.4165Z%27 fill=%27%23222222%27/%3E%3Cpath d=%27M6.83337 3.58317C6.83337 3.89259 6.95629 4.18934 7.17508 4.40813C7.39388 4.62692 7.69062 4.74984 8.00004 4.74984C8.30946 4.74984 8.60621 4.62692 8.825 4.40813C9.04379 4.18934 9.16671 3.89259 9.16671 3.58317C9.16671 3.27375 9.04379 2.97701 8.825 2.75821C8.60621 2.53942 8.30946 2.4165 8.00004 2.4165C7.69062 2.4165 7.39388 2.53942 7.17508 2.75821C6.95629 2.97701 6.83337 3.27375 6.83337 3.58317Z%27 fill=%27currentColor%27/%3E%3C/svg%3E");
}

The column menu button is always visible, but if you want it to appear only when the mouse cursor is over the header, apply additional styling to th .relative:hover .changeType.

JavaScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
// to differentiate this example's CSS from other examples on this page
className="customFilterButtonExample2"
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
// to differentiate this example's CSS from other examples on this page
className="customFilterButtonExample2"
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
CSS
/* hide the column menu button by default */
.customFilterButtonExample2 .changeType {
visibility: hidden;
}
/* show the column menu button on hover */
.customFilterButtonExample2 th .relative:hover .changeType {
visibility: visible;
}

Change the width of the filter menu

If the text data in your columns is too long to fit in the filters container, you can adjust the column menu’s width for better user experience. You can achieve this with by styling .htDropdownMenu table.htCore.

.handsontable .htDropdownMenu table.htCore {
width: 300px !important;
}

Exclude rows from filtering

You can exclude any number of top or bottom rows from filtering.

In the following demo, the first and the last row are frozen, and filtering doesn’t affect them.

JavaScript
// you need `useRef` to call Handsontable's instance methods
import { useRef } from 'react';
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotTableComponentRef = useRef(null);
const exclude = () => {
const hotInstance = hotTableComponentRef.current?.hotInstance;
// @ts-ignore
const filtersRowsMap = hotInstance?.getPlugin('filters').filtersRowsMap;
filtersRowsMap.setValueAtIndex(0, false);
filtersRowsMap.setValueAtIndex(filtersRowsMap.getLength() - 1, false);
};
return (<HotTable ref={hotTableComponentRef} data={[
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: 11,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: 0,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: 1,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '13:23',
inStock: 3,
},
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: 5,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: 22,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: 13,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: 0,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '13:23',
inStock: 14,
},
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: 16,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: 18,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: 3,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: 0,
},
{
brand: 'Vinte',
model: 'ML Road Frame-W',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: 2,
},
]} columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'numeric',
data: 'inStock',
className: 'htCenter',
},
]} height={200} colWidths={[120, 150, 120, 140, 120, 120]} fixedRowsTop={1} fixedRowsBottom={1} colHeaders={true} filters={true} dropdownMenu={true} afterFilter={exclude} autoWrapRow={true} autoWrapCol={true} licenseKey="non-commercial-and-evaluation"/>);
};
export default ExampleComponent;
TypeScript
// you need `useRef` to call Handsontable's instance methods
import { useRef } from 'react';
import { HotTable, HotTableRef } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotTableComponentRef = useRef<HotTableRef>(null);
const exclude = () => {
const hotInstance = hotTableComponentRef.current?.hotInstance;
// @ts-ignore
const filtersRowsMap = hotInstance?.getPlugin('filters').filtersRowsMap;
filtersRowsMap.setValueAtIndex(0, false);
filtersRowsMap.setValueAtIndex(filtersRowsMap.getLength() - 1, false);
};
return (
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: 11,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: 0,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: 1,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '13:23',
inStock: 3,
},
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: 5,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: 22,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: 13,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: 0,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '13:23',
inStock: 14,
},
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: 16,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: 18,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: 3,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: 0,
},
{
brand: 'Vinte',
model: 'ML Road Frame-W',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: 2,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'numeric',
data: 'inStock',
className: 'htCenter',
},
]}
height={200}
colWidths={[120, 150, 120, 140, 120, 120]}
fixedRowsTop={1}
fixedRowsBottom={1}
colHeaders={true}
filters={true}
dropdownMenu={true}
afterFilter={exclude}
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

If you use bindRowsWithHeaders to keep row headers tied to a row’s position in the source data, filtering doesn’t affect that binding. Filtering removes non-matching rows from view instead of renumbering the rows that remain, so bound row headers keep pointing to the same row after you filter or clear a filter.

Server-side filtering

You can decide to use Handsontable as an intuitive filtering interface, but perform the actual filtering on the server.

To help you with that, Handsontable’s beforeFilter() hook allows you to:

  • Gather information about the filters that the user wants to apply, to send it to the server.
  • Disable filtering on the front end, so it doesn’t interfere with filtering on the server.

In the following demo, click on one of the column menu buttons (▼) and play around with filtering by selecting values or conditions-based criteria. After you click OK, the ▼ button turns green to indicate filtering, but the data is not filtered. Instead, the information about the specified filters is logged to the console.

JavaScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height="auto"
// `beforeFilter()` is a Handsontable hook
// it's fired after Handsontable gathers information about the filters, but before the filters are applied
beforeFilter={function (conditionsStack) {
// gather information about the filters
console.log(`The amount of filters: ${conditionsStack.length}`);
// when the filters are cleared, the conditions stack is empty
const [lastChanged] = conditionsStack;
if (lastChanged) {
console.log(`The last changed column index: ${lastChanged.column}`);
console.log(`The amount of filters added to this column: ${lastChanged.conditions.length}`);
// the list of filter conditions
console.log(lastChanged.conditions);
}
// return `false` to disable filtering on the client side
return false;
}}
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height="auto"
// `beforeFilter()` is a Handsontable hook
// it's fired after Handsontable gathers information about the filters, but before the filters are applied
beforeFilter={function (conditionsStack) {
// gather information about the filters
console.log(`The amount of filters: ${conditionsStack.length}`);
// when the filters are cleared, the conditions stack is empty
const [lastChanged] = conditionsStack;
if (lastChanged) {
console.log(`The last changed column index: ${lastChanged.column}`);
console.log(`The amount of filters added to this column: ${lastChanged.conditions.length}`);
// the list of filter conditions
console.log(lastChanged.conditions);
}
// return `false` to disable filtering on the client side
return false;
}}
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

Control filtering programmatically

You can control filtering at the grid’s runtime by using Handsontable’s hooks and API methods. This allows you to enable or disable filtering based on specific conditions. For example, you may create a user interface outside of the grid to manage Handsontable’s filtering behavior.

Enable or disable filtering programmatically

To enable or disable filtering programmatically, use the updateSettings() method.

const hotTableComponentRef = useRef(null);
hotTableComponentRef.current.hotInstance.updateSettings({
// enable filtering
filters: true,
// enable the column menu
dropdownMenu: true,
});
hotTableComponentRef.current.hotInstance.updateSettings({
// disable filtering
filters: false,
});

You can also enable or disable filtering for specific columns. For example, to enable filtering only for the first column:

const hotTableComponentRef = useRef(null);
hotTableComponentRef.current.hotInstance.updateSettings({
// enable filtering for all columns
filters: true,
// enable the column menu for all columns
// but display only the 'Filter by value' list and the 'OK' and
// 'Cancel' buttons
dropdownMenu: {
items: {
filter_by_value: {
// hide the 'Filter by value' list from all columns but the
// first one
hidden() {
return this.getSelectedRangeLast().to.col > 0;
},
},
filter_action_bar: {
// hide the 'OK' and 'Cancel' buttons from all columns but the
// first one
hidden() {
return this.getSelectedRangeLast().to.col > 0;
},
},
},
},
});

Filter data programmatically

To filter data programmatically, use the Filters plugin’s API. Remember to enable filtering first.

Mind that before you apply new filter conditions, you need to clear the previous ones with filters.clearConditions().

JavaScript
import { useRef } from 'react';
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotTableComponentRef = useRef(null);
const filterBelow200 = () => {
// get the `Filters` plugin, so you can use its API
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
// clear any existing filters
filters?.clearConditions();
// filter data by the 'Price' column (column at index 2)
// to display only items that are less than ('lt') $200
filters?.addCondition(2, 'lt', [200]);
filters?.filter();
};
const filterAbove200 = () => {
// get the `Filters` plugin, so you can use its API
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
filters?.clearConditions();
// display only items that are more than ('gt') $200
filters?.addCondition(2, 'gt', [200]);
filters?.filter();
};
const clearAllFilters = () => {
// get the `Filters` plugin, so you can use its API
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
// clear all filters
filters?.clearConditions();
filters?.filter();
};
return (
<>
<div className="example-controls-container">
<div className="controls">
<button onClick={filterBelow200}>Show items &lt; $200</button>
<button onClick={filterAbove200}>Show items &gt; $200</button>
<button onClick={clearAllFilters}>Clear filters</button>
</div>
</div>
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
</>
);
};
export default ExampleComponent;
TypeScript
import { useRef } from 'react';
import { HotTable, HotTableRef } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotTableComponentRef = useRef<HotTableRef>(null);
const filterBelow200 = () => {
// get the `Filters` plugin, so you can use its API
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
// clear any existing filters
filters?.clearConditions();
// filter data by the 'Price' column (column at index 2)
// to display only items that are less than ('lt') $200
filters?.addCondition(2, 'lt', [200]);
filters?.filter();
};
const filterAbove200 = () => {
// get the `Filters` plugin, so you can use its API
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
filters?.clearConditions();
// display only items that are more than ('gt') $200
filters?.addCondition(2, 'gt', [200]);
filters?.filter();
};
const clearAllFilters = () => {
// get the `Filters` plugin, so you can use its API
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
// clear all filters
filters?.clearConditions();
filters?.filter();
};
return (
<>
<div className="example-controls-container">
<div className="controls">
<button onClick={filterBelow200}>Show items &lt; $200</button>
<button onClick={filterAbove200}>Show items &gt; $200</button>
<button onClick={clearAllFilters}>Clear filters</button>
</div>
</div>
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
</>
);
};
export default ExampleComponent;

Combine multiple filter conditions

A column can hold more than one condition. The fourth argument of filters.addCondition() sets the logical operation that joins the conditions for that column:

  • 'conjunction' — AND logic. Every condition must match. This is the default, so you can omit the argument.
  • 'disjunction' — OR logic. At least one condition must match.

Apply the same operation to all conditions in a column. Don’t mix 'conjunction' and 'disjunction' for a single column.

The following snippet keeps the rows where the price (column index 2) is less than 200 or greater than 400.

const filters = hotTableComponentRef.current.hotInstance.getPlugin('filters');
// clear the column's existing conditions first
filters.clearConditions(2);
// match rows where the price is less than 200 OR greater than 400
filters.addCondition(2, 'lt', [200], 'disjunction');
filters.addCondition(2, 'gt', [400], 'disjunction');
filters.filter();

The dropdown menu shows at most two conditions plus one Filter by value condition per column. If you add more conditions than that, they still filter the data but don’t appear in the menu. See Known limitations.

Clear a column filter

To clear the conditions for a single column, call filters.removeConditions(column). This is the programmatic equivalent of selecting the None operator in the column menu.

const filters = handsontableInstance.getPlugin('filters');
// clear the filter for the column at index 2
filters.removeConditions(2);
filters.filter();

To clear the conditions for every column at once, call filters.clearConditions() without an argument.

Adding the 'none' condition with addCondition() has no filtering effect, because 'none' matches every row:

// matches all rows -- the column stays unfiltered
filters.addCondition(2, 'none', []);
filters.filter();

Save and restore filter settings

To persist a user’s filter selections (for example, across page reloads or between sessions), export the current conditions with filters.exportConditions(), and store the result. To reapply them later, pass the stored array to filters.importConditions(), then call filters.filter() to apply the change.

Unlike addCondition(), which takes a visual column index, the column property in each exported condition is a physical column index.

JavaScript
import { useRef } from 'react';
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotTableComponentRef = useRef(null);
const savedConditions = useRef([]);
const applySampleFilter = () => {
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
filters?.clearConditions();
// filter the 'Price' column (column at index 2) for items over $200
filters?.addCondition(2, 'gt', [200]);
filters?.filter();
};
const saveFilters = () => {
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
// `exportConditions()` returns the current conditions, keyed by physical column index
savedConditions.current = filters?.exportConditions() ?? [];
};
const clearFilters = () => {
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
filters?.clearConditions();
filters?.filter();
};
const restoreFilters = () => {
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
// `importConditions()` expects the same physical-column-indexed structure
filters?.importConditions(savedConditions.current);
filters?.filter();
};
return (
<>
<div className="example-controls-container">
<div className="controls">
<button onClick={applySampleFilter}>Filter: price &gt; $200</button>
<button onClick={saveFilters}>Save filter settings</button>
<button onClick={clearFilters}>Clear filters</button>
<button onClick={restoreFilters}>Restore filter settings</button>
</div>
</div>
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
</>
);
};
export default ExampleComponent;
TypeScript
import { useRef } from 'react';
import { HotTable, HotTableRef } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
import type { ColumnConditions } from 'handsontable/plugins/filters';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotTableComponentRef = useRef<HotTableRef>(null);
const savedConditions = useRef<ColumnConditions[]>([]);
const applySampleFilter = () => {
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
filters?.clearConditions();
// filter the 'Price' column (column at index 2) for items over $200
filters?.addCondition(2, 'gt', [200]);
filters?.filter();
};
const saveFilters = () => {
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
// `exportConditions()` returns the current conditions, keyed by physical column index
savedConditions.current = filters?.exportConditions() ?? [];
};
const clearFilters = () => {
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
filters?.clearConditions();
filters?.filter();
};
const restoreFilters = () => {
const filters = hotTableComponentRef.current?.hotInstance?.getPlugin('filters');
// `importConditions()` expects the same physical-column-indexed structure
filters?.importConditions(savedConditions.current);
filters?.filter();
};
return (
<>
<div className="example-controls-container">
<div className="controls">
<button onClick={applySampleFilter}>Filter: price &gt; $200</button>
<button onClick={saveFilters}>Save filter settings</button>
<button onClick={clearFilters}>Clear filters</button>
<button onClick={restoreFilters}>Restore filter settings</button>
</div>
</div>
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
</>
);
};
export default ExampleComponent;

Get filtered data

After filtering, getData() returns only the rows that pass the current filters, because filtering removes non-matching rows from the grid’s data map instead of merely hiding them. To read every row regardless of the active filters, use getSourceData().

The following demo uses the afterFilter() hook to compare the two methods every time the filters change.

JavaScript
import { useRef, useState } from 'react';
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotTableComponentRef = useRef(null);
const [rowCountInfo, setRowCountInfo] = useState('');
const updateRowCountInfo = () => {
const hotInstance = hotTableComponentRef.current?.hotInstance;
if (!hotInstance) {
return;
}
// `getData()` returns only the rows that pass the current filters
// `getSourceData()` always returns every row, filtered or not
setRowCountInfo(`Showing ${hotInstance.getData().length} of ${hotInstance.getSourceData().length} rows.`);
};
return (
<>
<p>{rowCountInfo}</p>
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
afterInit={updateRowCountInfo}
afterFilter={updateRowCountInfo}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
</>
);
};
export default ExampleComponent;
TypeScript
import { useRef, useState } from 'react';
import { HotTable, HotTableRef } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotTableComponentRef = useRef<HotTableRef>(null);
const [rowCountInfo, setRowCountInfo] = useState('');
const updateRowCountInfo = () => {
const hotInstance = hotTableComponentRef.current?.hotInstance;
if (!hotInstance) {
return;
}
// `getData()` returns only the rows that pass the current filters
// `getSourceData()` always returns every row, filtered or not
setRowCountInfo(`Showing ${hotInstance.getData().length} of ${hotInstance.getSourceData().length} rows.`);
};
return (
<>
<p>{rowCountInfo}</p>
<HotTable
ref={hotTableComponentRef}
data={[
{
brand: 'Jetpulse',
model: 'Racing Socks',
price: 30,
sellDate: '2023-10-11',
sellTime: '01:23',
inStock: false,
},
{
brand: 'Gigabox',
model: 'HL Mountain Frame',
price: 1890.9,
sellDate: '2023-05-03',
sellTime: '11:27',
inStock: false,
},
{
brand: 'Camido',
model: 'Cycling Cap',
price: 130.1,
sellDate: '2023-03-27',
sellTime: '03:17',
inStock: true,
},
{
brand: 'Chatterpoint',
model: 'Road Tire Tube',
price: 59,
sellDate: '2023-08-28',
sellTime: '08:01',
inStock: true,
},
{
brand: 'Eidel',
model: 'HL Road Tire',
price: 279.99,
sellDate: '2023-10-02',
sellTime: '01:23',
inStock: true,
},
]}
columns={[
{
title: 'Brand',
type: 'text',
data: 'brand',
},
{
title: 'Model',
type: 'text',
data: 'model',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
},
]}
// enable filtering
filters={true}
// enable the column menu
dropdownMenu={true}
afterInit={updateRowCountInfo}
afterFilter={updateRowCountInfo}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
</>
);
};
export default ExampleComponent;

Import the filtering module

You can reduce the size of your bundle by importing and registering only the modules that you need.

To use filtering, you need only the following modules:

// import the base module
import Handsontable from 'handsontable/base';
// import the filtering plugins
import { registerPlugin, Filters, DropdownMenu } from 'handsontable/plugins';
// register the filtering plugins
registerPlugin(Filters);
registerPlugin(DropdownMenu);

Known limitations

At the moment, filtering comes with the following limitations:

  • There is no easy way to add custom filter operators to the user interface.
  • The list of values that you can filter by is generated automatically and there’s no supported way of modifying it.
  • The filter’s dropdown menu has a limited capacity per column: at most 2 regular conditions and 1 “filter by value” condition. If you add more conditions programmatically via addCondition(), the extra conditions are applied to the data but are not visible or editable in the dropdown menu.

Use keyboard and pointer interactions together to control the filtering UI:

  • Press Tab and Shift+Tab to move between filtering components, such as the search input, Select all, Clear all, condition controls, and action bar controls.
  • When the search input is focused, press Arrow up and Arrow down to move through the Filter by value list.
  • When Select all or Clear all is focused, press Enter or Space to run the action.
  • If you hover a non-filter menu item (for example, Clear column), the filter-components focus order resets. The next Tab focuses the first filter component.

For a reference list of filter-related shortcuts, see Keyboard shortcuts.

WindowsmacOSActionExcelSheets
Alt+A+AClear all filters

API reference

For the list of options, methods, and Handsontable hooks related to filtering, see the following API reference pages:

Plugins

Troubleshooting

Didn’t find what you need? Try this: