# Filtering

> Learn how to configure client-side and server-side filtering in Infinite Table for React

Canonical page: https://infinite-table.com/docs/learn/filtering/

Filtering allows you to limit the rows available in the table.

Both client-side and server-side filtering are available in Infinite Table - but the way the are configured is pretty similar, so this page documents the common parts, while pointing to the respective pages for the differences.

## Configuring Filters for Columns

The most common way to use filtering in Infinite Table is by configuring filters for columns (this works both for client-side and server-side filtering).

You specify an uncontrolled [`defaultFilterValue`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultFilterValue) on the `<DataSource />` component (or the controlled version, [`filterValue`](https://infinite-table.com/docs/reference/datasource-props/index.md#filterValue)) and the specified value will be used as the initial filter.

Based on the [column type](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.type), the correct filter editor is displayed in the column header, along with the correct operator icon. In the UI, you can change the operator being used for the filter.

```tsx title="Specifying an initial filter value for the DataSource"
<DataSource<Developer>
  data={...}
  defaultFilterValue={[
    {
      field: 'age',
      filter: {
        operator: 'gt',
        value: 30,
        type: 'number'
      }
    }
  ]}
>
  <InfiniteTable<Developer>
    columns={...}
  />
</DataSource>
```

If you don't need to specify some initial filters, but want the column filter bar to be visible, you need to specify `defaultFilterValue = []` (or the controlled `filterValue = []`).

Specifying any of those props will make the column filter bar visible.

Whenever filters change, [`onFilterChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onFilterChange) will be called with the new filter value - note however, it might not be called immediately, due to the [`filterDelay`](https://infinite-table.com/docs/reference/datasource-props/index.md#filterDelay) prop.

The above snippet will show a `number` filter for the `age` column. There are two filter types available at this stage in Infinite Table:

- `string` - with the following operators available: `contains`, `eq`, `startsWith` and `endsWith`
- `number` - with the following operators available: `eq`,`neq`, `gt`, `gte`, `lt` and `lte`

## Defining Filterable Columns

By default, all columns are filterable.

If you want to make columns by default not filterable, use the [`columnDefaultFilterable`](https://infinite-table.com/docs/reference/infinite-table-props.md#columnDefaultFilterable) prop and set it to `false`.

You can specifically configure each column by using the [defaultFilterable](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.defaultFilterable) property - this overrides the global [`columnDefaultFilterable`](https://infinite-table.com/docs/reference/infinite-table-props.md#columnDefaultFilterable) prop.

## Defining a Filter Type for a Column

Besides being filterable, a column can decide what type of filter it will display.

Use the [`columns.type`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.type) property to specify the type of filter the column will use. Using the `type` property also configures the data type of the column, which in turn determines the sort type.

If the type of filter you want to show does not match the column [type](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.type), you can specify the filter with the [column.filterType](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.filterType) property. Only use this when the type of the data differs from the type of the filter (eg: you have a numeric column, with a custom filter type).

## Understanding Filter Types

A filter type is a concept that defines how a certain type of data is to be filtered.
A filter type will have

- a `key` - the key used to define the filter in the [`filterTypes`](https://infinite-table.com/docs/reference/datasource-props/index.md#filterTypes) object
- a `label`,
- an array of values considered to be empty values - when any of these values is used in the filter, the filter will not be applied.
- an array of `operators`
- a default operator.

Let's imagine you have a `DataSource` with developers, each with a `salary` column, and for that column you want to allow `>`, `>=`, `<` and `<=` comparisons (operators).

For this, you would define the following filter type:

```tsx
const filterTypes = {
  income: {
    label: 'Income',
    emptyValues: ['', null, undefined],
    defaultOperator: 'gt',
    operators: [
      {
        name: 'gt',
        label: 'Greater than',
        fn: ({ currentValue, filterValue, emptyValues }) => {
          if (emptyValues.has(currentValue)) {
            return true;
          }
          return currentValue > filterValue;
        },
      },
      {
        name: 'gte',
        //...
      },
      {
        name: 'lt',
        //...
      },
      {
        name: 'lte',
        //...
      },
    ],
  },
};
```

Each operator for a certain filter type needs to at least have a `name` and `fn` defined. The `fn` property is a function that will be called when client-side filtering is enabled, with an object that has the following properties:

- `currentValue` - the cell value of the current row for the column being filtered
- `filterValue` - the value of the filter editor
- `emptyValues` - the array of values considered to be empty values for the filter type
- `data` - the current row data object - `typeof DATA_TYPE`
- `index` - the index of the current row in the table - `number`
- `dataArray` - the array of all rows originally in the table - `typeof DATA_TYPE[]`
- `field?` - the field the current column is bound to (can be undefined if the column is not bound to a field)

**Example: Client-side filtering in action with custom filter type**

The `salary` column has a custom filter type, with the following operators: `gt`, `gte`, `lt` and `lte`.

```ts
import * as React from 'react';

import {
  DataSourceData,
  DataSource,
  InfiniteTable,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;

  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';

  salary: number;
};

const data: DataSourceData<Developer> = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + `/developers1k-sql?`)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: {
    field: 'id',
    type: 'number',
    defaultWidth: 100,
  },
  salary: {
    defaultFilterable: true,
    field: 'salary',
    type: 'number',
    filterType: 'salary',
  },

  firstName: {
    field: 'firstName',
  },
  stack: { field: 'stack' },
  currency: { field: 'currency', defaultFilterable: false },
};

function getIcon(icon: string) {
  return () => (
    <div
      style={{
        width: 20,
        display: 'flex',
        justifyContent: 'center',
        flexFlow: 'row',
      }}
    >
      {icon}
    </div>
  );
}

const domProps = {
  style: {
    height: '100%',
  },
};
export default () => {
  return (
    <>
      <React.StrictMode>
        <DataSource<Developer>
          data={data}
          primaryKey="id"
          defaultFilterValue={[]}
          filterDelay={0}
          filterMode="local"
          filterTypes={{
            salary: {
              defaultOperator: 'gt',
              emptyValues: ['', null, undefined],
              operators: [
                {
                  name: 'gt',
                  label: 'Greater Than',
                  components: {
                    Icon: getIcon('>'),
                  },
                  fn: ({ currentValue, filterValue }) => {
                    return currentValue > filterValue;
                  },
                },
                {
                  name: 'gte',
                  components: {
                    Icon: getIcon('>='),
                  },
                  label: 'Greater Than or Equal',
                  fn: ({ currentValue, filterValue }) => {
                    return currentValue >= filterValue;
                  },
                },
                {
                  name: 'lt',
                  components: {
                    Icon: getIcon('<'),
                  },
                  label: 'Less Than',
                  fn: ({ currentValue, filterValue }) => {
                    return currentValue < filterValue;
                  },
                },
                {
                  name: 'lte',
                  components: {
                    Icon: getIcon('<='),
                  },
                  label: 'Less Than or Equal',
                  fn: ({ currentValue, filterValue }) => {
                    return currentValue <= filterValue;
                  },
                },
              ],
            },
          }}
        >
          <InfiniteTable<Developer>
            debugId="filter-custom-filter-type-example"
            domProps={domProps}
            columnDefaultWidth={150}
            columnMinWidth={50}
            columns={columns}
          />
        </DataSource>
      </React.StrictMode>
    </>
  );
};
```

## Specifying the filter mode

As already mentioned, filtering can happen either client-side or server-side. If the DataSource [`data`](https://infinite-table.com/docs/reference/datasource-props/index.md#data) property is a function (and not an array or a `Promise`), then the filtering will happen server-side by default.

However, you can explicitly specify where the filtering should happen by setting the [`filterMode`](https://infinite-table.com/docs/reference/datasource-props/index.md#filterMode) property on the `<DataSource />` component - possible values are

- `filterMode="local"` - filtering will happen client-side
- `filterMode="remote"` - filtering will happen remotely and the [`filterValue`](https://infinite-table.com/docs/reference/datasource-props/index.md#filterValue) will be passed as a property to the parameter object sent to the [`data`](https://infinite-table.com/docs/reference/datasource-props/index.md#data) function.

Explicitly specify [`filterMode`](https://infinite-table.com/docs/reference/datasource-props/index.md#filterMode) as either `"local"` or `"remote"` if you want to change the default behavior.

## Filtering Columns Not Bound to a Field

If a column is not bound to a `field`, it can still be used for filtering, even client-side filtering, if it is configured with a [`columns.valueGetter`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter).

If you don't need a default filter value, the [`filterValue`](https://infinite-table.com/docs/reference/datasource-props/index.md#filterValue) that's set when the user interacts with the column filter will use the column [valueGetter](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter) to filter values.

If however, you need initial filtering by that column, the [`filterValue`](https://infinite-table.com/docs/reference/datasource-props/index.md#filterValue) needs to specify a `valueGetter` itself.

```tsx
defaultFilterValue={[
  {
    id: 'salary',
    valueGetter: ({ data }) => data.salary,
    filter: {
      operator: 'gt',
      value: '',
      type: 'number',
    }
  },
]}
```

**Example: Filtering a column not bound to a field**

The `salary` column is not bound to a `field` - however, it can still be used for filtering, as it's configured with a `valueGetter`.

```ts
import * as React from 'react';

import {
  DataSourceData,
  InfiniteTable,
  InfiniteTablePropColumns,
  DataSource,
} from '@infinite-table/infinite-react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;

  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';

  salary: number;
};

const data: DataSourceData<Developer> = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + `/developers1k-sql?`)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: {
    field: 'id',
    type: 'number',
    defaultWidth: 70,
    defaultFilterable: false,
  },

  salary: {
    // we're intentionally using not binding this column to a `field`
    type: 'number',
    header: 'Salary',
    valueGetter: ({ data }) => data.salary,
  },

  firstName: {
    field: 'firstName',
  },
  stack: { field: 'stack' },
  currency: { field: 'currency' },
};

const domProps = {
  style: {
    height: '100%',
  },
};
export default () => {
  return (
    <>
      <React.StrictMode>
        <DataSource<Developer>
          data={data}
          primaryKey="id"
          defaultFilterValue={[
            // if you want the salary column to be filtered by default
            // you need to pass a valueGetter to the filter value
            // if you don't need a default filter, when you start filtering by
            // the column, the filter value will use the valueGetter of the column
            {
              id: 'salary',
              valueGetter: ({ data }) => data.salary,
              filter: {
                operator: 'gt',
                value: '',
                type: 'number',
              },
            },
          ]}
          filterDelay={0}
          filterMode="local"
        >
          <InfiniteTable<Developer>
            debugId="filter-column-with-id-example"
            domProps={domProps}
            columnDefaultWidth={150}
            columnMinWidth={50}
            columns={columns}
          />
        </DataSource>
      </React.StrictMode>
    </>
  );
};
```

## Customizing the Filter Icon for Columns

Columns can customize the filter icon by using the [`columns.renderFilterIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderFilterIcon) property.

**Example: Custom filter icons for salary and name columns**

The `salary` column will show a bolded label when filtered.

The `firstName` column will show a custom filter icon when filtered.

```ts
import * as React from 'react';

import {
  DataSourceData,
  InfiniteTable,
  InfiniteTablePropColumns,
  DataSource,
} from '@infinite-table/infinite-react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;

  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';

  salary: number;
};

const data: DataSourceData<Developer> = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + `/developers1k-sql?`)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: {
    field: 'id',
    type: 'number',
    defaultWidth: 100,
  },
  salary: {
    field: 'salary',
    type: 'number',
    header: ({ filtered }) => {
      return filtered ? <b>Salary</b> : 'Salary';
    },

    renderFilterIcon: () => {
      return null;
    },
  },

  firstName: {
    field: 'firstName',
    renderFilterIcon: ({ filtered }) => {
      return filtered ? '🔥' : '';
    },
  },
  stack: { field: 'stack' },
  currency: { field: 'currency' },
};

const domProps = {
  style: {
    height: '100%',
  },
};
export default () => {
  return (
    <>
      <React.StrictMode>
        <DataSource<Developer>
          data={data}
          primaryKey="id"
          defaultFilterValue={[]}
          filterDelay={0}
          filterMode="local"
        >
          <InfiniteTable<Developer>
            debugId="column-filter-icon-example"
            domProps={domProps}
            columnDefaultWidth={150}
            columnMinWidth={50}
            columns={columns}
          />
        </DataSource>
      </React.StrictMode>
    </>
  );
};
```

Learn how to use filtering in the browser.

Figure out how to use filtering with server-side integration.
