---
product: "AG Grid"
title: "Advanced Filter - Custom Filter Options"
description: "Custom Filter Options defined for a column are also offered in the Advanced Filter, so an expression can use the same options as the column filter."
enterprise: true
framework: javascript
version: "36.2.0"
related:
    - title: "Columns & Filter Options"
      url: "https://www.ag-grid.com/javascript-data-grid/filter-advanced-columns/"
    - title: "Input & Builder"
      url: "https://www.ag-grid.com/javascript-data-grid/filter-advanced-input-builder/"
    - title: "Filter Model / API"
      url: "https://www.ag-grid.com/javascript-data-grid/filter-advanced-api/"
llms: "https://www.ag-grid.com/llms.txt"
---

# Advanced Filter - Custom Filter Options

Custom Filter Options defined for a column are also offered in the Advanced Filter, so an expression can use the same options as the column filter.

## Configuring Custom Filter Options

The Advanced Filter accepts [Custom Filter Options](https://www.ag-grid.com/javascript-data-grid/filter-conditions/#custom-filter-options) the same way as Column Filters. Each Custom Filter Option is an `IFilterOptionDef` with the following properties.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `displayKey` | `string` |  |  |  |
| `displayName` | `string` |  |  |  |
| `predicate` | `Function` |  |  |  |
| `numberOfInputs` | `0 \| 1 \| 2` |  |  |  |

Options taking two values are validated like `is between`: the first value must be less than the second, or equal to it where `inRangeInclusive = true`.

The `predicate` only runs with the Client-Side Row Model. With the Server-Side Row Model the option is sent to the server as its `displayKey` in the filter model, as for the [Set Filter options](https://www.ag-grid.com/javascript-data-grid/filter-advanced-columns/#set-filters).

The following example demonstrates custom filter options taking different numbers of values:

- The **Athlete** column has `Starts With A` (no values) and `Does Not Start With` (one value).
- The **Age** column has `Even Numbers` (no values) and `Between (Exclusive)` (two values).
- The **Date** column has `Leap Year` (no values) and `Between (Exclusive)` (two dates).

#### Custom Filter Options

```ts
import {
  ClientSideRowModelModule,
  DateFilterModule,
  GridApi,
  GridOptions,
  IDateFilterParams,
  INumberFilterParams,
  ITextFilterParams,
  ModuleRegistry,
  NumberFilterModule,
  TextFilterModule,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import {
  AdvancedFilterModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableDevValidations();
}

ModuleRegistry.registerModules([
  TextFilterModule,
  NumberFilterModule,
  DateFilterModule,
  AdvancedFilterModule,
  ClientSideRowModelModule,
  ColumnMenuModule,
  ContextMenuModule,
]);

let gridApi: GridApi<IOlympicData>;

const athleteFilterParams: ITextFilterParams = {
  filterOptions: [
    "contains",
    {
      displayKey: "startsWithA",
      displayName: "Starts With A",
      numberOfInputs: 0,
      predicate: (_, cellValue) =>
        cellValue != null && cellValue.startsWith("A"),
    },
    {
      displayKey: "notStartsWith",
      displayName: "Does Not Start With",
      numberOfInputs: 1,
      predicate: ([filterValue], cellValue) =>
        cellValue != null &&
        !cellValue.toLowerCase().startsWith(String(filterValue).toLowerCase()),
    },
  ],
};

const ageFilterParams: INumberFilterParams = {
  filterOptions: [
    "equals",
    {
      displayKey: "evenNumbers",
      displayName: "Even Numbers",
      numberOfInputs: 0,
      predicate: (_, cellValue) => cellValue != null && cellValue % 2 === 0,
    },
    {
      displayKey: "betweenExclusive",
      displayName: "Between (Exclusive)",
      numberOfInputs: 2,
      predicate: ([from, to], cellValue) =>
        cellValue != null && cellValue > from && cellValue < to,
    },
  ],
};

const dateFilterParams: IDateFilterParams = {
  filterOptions: [
    "equals",
    {
      displayKey: "leapYear",
      displayName: "Leap Year",
      numberOfInputs: 0,
      predicate: (_, cellValue) => {
        if (cellValue == null) {
          return false;
        }
        const year = Number(cellValue.split("-")[0]);
        return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
      },
    },
    {
      displayKey: "betweenExclusive",
      displayName: "Between (Exclusive)",
      numberOfInputs: 2,
      predicate: ([from, to], cellValue) => {
        if (cellValue == null) {
          return false;
        }
        // Built as a local date: the filter's own values are local midnight, and
        // `new Date('YYYY-MM-DD')` would be UTC midnight, so the two would not line up.
        const [year, month, day] = cellValue.split("-").map(Number);
        const cellDate = new Date(year, month - 1, day);
        return cellDate > from && cellDate < to;
      },
    },
  ],
};

const gridOptions: GridOptions<IOlympicData> = {
  columnDefs: [
    { field: "athlete", filterParams: athleteFilterParams },
    { field: "age", minWidth: 120, filterParams: ageFilterParams },
    {
      field: "date",
      filter: "agDateColumnFilter",
      filterParams: dateFilterParams,
    },
    { field: "sport" },
    { field: "gold" },
  ],
  defaultColDef: {
    flex: 1,
    minWidth: 180,
    filter: true,
  },
  enableAdvancedFilter: true,
};

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
  .then((response) => response.json())
  .then((data: IOlympicData[]) =>
    gridApi!.setGridOption(
      "rowData",
      // The supplied dates are `dd/mm/yyyy` strings, which is a text column. Convert them to
      // `yyyy-mm-dd` so the column is a Date (String) one and its options filter on dates.
      data.map((rowData) => {
        const [day, month, year] = rowData.date.split("/");
        return {
          ...rowData,
          date: `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`,
        };
      }),
    ),
  );
```

[Live example: Custom Filter Options](https://www.ag-grid.com/examples/filter-advanced-custom-filter-options/custom-filter-options/typescript/)

```js
const gridOptions = {
    columnDefs: [
        {
            field: 'age',
            filterParams: {
                filterOptions: [
                    'equals',
                    {
                        displayKey: 'evenNumbers',
                        displayName: 'Even Numbers',
                        numberOfInputs: 0,
                        predicate: (_values, cellValue) => cellValue != null && cellValue % 2 === 0,
                    },
                    {
                        displayKey: 'betweenExclusive',
                        displayName: 'Between (Exclusive)',
                        numberOfInputs: 2,
                        predicate: ([from, to], cellValue) => cellValue != null && cellValue > from && cellValue < to,
                    },
                ],
            },
        },
    ],

    // other grid options ...
}
```

## Using Custom Filter Options in the Advanced Filter Input

Custom Filter Options are typed into the Advanced Filter input using their `displayName`, followed by its values. Using the options from the example above these are some example inputs:

```shell
[Athlete] Starts With A
[Athlete] Does Not Start With "Michael"
[Age] Even Numbers
[Age] Between (Exclusive) (30, 40)
[Date] Between (Exclusive) ("2008-08-20", "2008-08-25")
```

Values are quoted according to the column's Cell Data Type, as for the built-in options: numbers are unquoted, everything else is quoted. Two values are separated by a comma; the surrounding brackets are optional. Where the `displayKey` has a [localised](https://www.ag-grid.com/javascript-data-grid/localisation/) entry, that text is used as the option name instead.

A `displayKey` is resolved against the column being filtered, so different columns can reuse the same key. Reusing an Option Key from the [table of standard options](https://www.ag-grid.com/javascript-data-grid/filter-advanced-columns/#filter-parameters), for example `contains`, replaces that built-in option for the column, the same as it does in the column filter.

## Filter Model

A condition using a Custom Filter Option stores the `displayKey` in `type`, and the values in `filter` and `filterTo`. See [Filter Model / API](https://www.ag-grid.com/javascript-data-grid/filter-advanced-api/) for saving and restoring the Advanced Filter state.

```js
const advancedFilterModel = {
    filterType: 'number',
    colId: 'age',
    type: 'betweenExclusive',
    filter: 30,
    filterTo: 40,
};
```
