# Extending existing filters

> Learn how to extend existing filters and filter types for your Infinite Table React DataGrid

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

By default `InfiniteTable` has the following default filter types:

- `string`
- `number`

and each of them has a collection of operators that are supported - see [`filterTypes`](https://infinite-table.com/docs/reference/datasource-props/index.md#filterTypes) for the respective list of supported operators.

You may find those operators limiting - but it's easy to extend them and add new operators or even new filter types.

## Adding new operators to existing filter types

You can import `defaultFilterTypes` from the root of the package.

```ts title="Adding a new operator to the string filter type"
import { defaultFilterTypes } from '@infinite-table/infinite-react';

// add new operators for the `string` filter type
defaultFilterTypes.string.operators.push({
  name: 'notContains',
  component: { Icon: ReactComponentForIcon }
  label: 'Not Contains',
  fn: ({currentValue, filterValue }) => {
    return typeof currentValue === 'string' &&
            typeof filterValue == 'string' &&
            !currentValue.toLowerCase().includes(filterValue.toLowerCase())
  }
})
```

When you import the named `defaultFilterTypes` value and extend it, that will affect all `InfiniteTable` components in your application.

If you don't want that, you need to use the `filterTypes` prop of the `<DataSource />` component. Either build an entirely new object for `filterTypes`, or start by cloning `defaultFilterTypes` and extend it.

**Example: Enhanced string filter type - new 'Not includes' operator**

The `string` columns have a new `Not includes` operator.

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

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

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

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

  salary: number;
};

defaultFilterTypes.string.operators.push({
  name: 'Not contains',
  label: 'Not Contains',
  fn: ({ currentValue, filterValue, emptyValues }) => {
    if (
      emptyValues.includes(currentValue) ||
      emptyValues.includes(filterValue)
    ) {
      return true;
    }
    return (
      typeof currentValue === 'string' &&
      typeof filterValue == 'string' &&
      !currentValue.toLowerCase().includes(filterValue.toLowerCase())
    );
  },
});

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,
  },

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

  salary: {
    field: 'salary',
    type: 'number',
  },
  currency: { field: 'currency', defaultFilterable: false },
};

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

## Adding new filter types

If the existing filter types are not enough, it's easy to add new ones.

As already mentioned, you can either update the value of `defaultFilterTypes` or use the `filterTypes` prop of the `<DataSource />` component. Updating the value `defaultFilterTypes` will affect all your `InfiniteTable` DataGrid components.

```ts title="Adding a new filter type by updating defaultFilterTypes"
import { defaultFilterTypes } from '@infinite-table/infinite-react';

defaultFilterTypes.bool = {
  defaultOperator: 'eq',
  emptyValues: [null],
  operators: [
    {
      name: 'eq',
      label: 'Equals',
      fn: ({ currentValue, filterValue }) => currentValue === filterValue,
    },
  ],
};
```

```ts title="Adding a new filter type by using the filterTypes prop"
import { DataSource } from '@infinite-table/infinite-react';

<DataSource
  filterTypes={{
    bool: {
      defaultOperator: 'eq',
      emptyValues: [null],
      operators: [
        {
          name: 'eq',
          label: 'Equals',
          fn: ({ currentValue, filterValue }) => currentValue === filterValue,
        },
      ],
    },
  }}
/>;
```

When passing `filterTypes` to the `<DataSource />` component, the object will be merged with the `defaultFilterTypes`. As a result, the existing `string` and `number` filterTypes will be preserved, unless explicitly overridden.

**Example: Writing a `bool` filter type with a custom filter editor**

The `canDesign` column is using a custom `bool` filter type with a custom filter editor.

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

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

const { CheckBox } = components;

type Developer = {
  id: number;
  firstName: string;
  canDesign: boolean;
  stack: string;
  hobby: string;
};
const dataSource: Developer[] = [
  {
    id: 1,
    firstName: 'John',

    canDesign: true,
    stack: 'frontend',
    hobby: 'gaming',
  },
  {
    id: 2,
    firstName: 'Jane',

    canDesign: false,
    stack: 'backend',
    hobby: 'reading',
  },
  {
    id: 3,
    firstName: 'Jack',

    canDesign: true,
    stack: 'frontend',
    hobby: 'gaming',
  },
  {
    id: 4,
    firstName: 'Jill',

    canDesign: false,
    stack: 'backend',
    hobby: 'reading',
  },
  {
    id: 5,
    firstName: 'Seb',

    canDesign: false,
    stack: 'backend',
    hobby: 'reading',
  },
];

const columns: InfiniteTablePropColumns<Developer> = {
  id: {
    field: 'id',
    type: 'number',
    defaultWidth: 100,
  },
  canDesign: {
    field: 'canDesign',
    filterType: 'bool',
    renderValue: ({ value }) => (value ? 'Yes' : 'No'),
  },
  firstName: {
    field: 'firstName',
  },
  stack: { field: 'stack' },
};

const domProps = {
  style: {
    height: '100%',
  },
};

function BoolFilterEditor() {
  const { value, setValue, className } =
    useInfiniteColumnFilterEditor<Developer>();

  return (
    <div className={className} style={{ textAlign: 'center' }}>
      <CheckBox
        checked={value}
        onChange={(newValue) => {
          if (value === true) {
            // after the value was true, make it go to indeterminate state
            newValue = null;
          }
          if (value === null) {
            // from indeterminate, goto false
            newValue = false;
          }
          setValue(newValue);
        }}
      />
    </div>
  );
}

export default () => {
  return (
    <>
      <React.StrictMode>
        <DataSource<Developer>
          data={dataSource}
          primaryKey="id"
          defaultFilterValue={[]}
          filterDelay={0}
          filterTypes={{
            bool: {
              defaultOperator: 'eq',
              emptyValues: [null],
              components: {
                FilterEditor: BoolFilterEditor,
                FilterOperatorSwitch: () => null,
              },
              operators: [
                {
                  name: 'eq',
                  label: 'Equals',
                  fn: ({ currentValue, filterValue }) =>
                    currentValue === filterValue,
                },
              ],
            },
          }}
        >
          <InfiniteTable<Developer>
            debugId="checkbox-filter-editor-example"
            domProps={domProps}
            columnDefaultWidth={150}
            columnMinWidth={50}
            columns={columns}
          />
        </DataSource>
      </React.StrictMode>
    </>
  );
};
```
