---
title: "External Filter"
framework: react
version: "36.1.0"
---

# External Filter

External filtering allows you to mix your own filtering logic with the grid's inbuilt filtering, without creating column-based filter components.

> **Warning**
>
> This form of filtering is only compatible with the Client-Side Row Model, see [Row Models](https://www.ag-grid.com/react-data-grid/row-models/) for more details.

## Implementing External Filtering

The example below shows external filters in action. There are two methods on `gridOptions` you need to implement: `isExternalFilterPresent` and `doesExternalFilterPass`.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `isExternalFilterPresent` | `IsExternalFilterPresent` |  |  | Grid calls this method to know if an external filter is present. Module: [`ExternalFilterModule`](https://www.ag-grid.com/react-data-grid/modules/). |
| `doesExternalFilterPass` | `DoesExternalFilterPass` |  |  | Should return `true` if external filter passes, otherwise `false`. Module: [`ExternalFilterModule`](https://www.ag-grid.com/react-data-grid/modules/). |

### isExternalFilterPresent

`isExternalFilterPresent` is called exactly once every time the grid senses a filter change. It should return `true` if external filtering is active or `false` otherwise. If you return `true`, `doesExternalFilterPass` will be called while filtering, otherwise `doesExternalFilterPass` will not be called.

### doesExternalFilterPass

`doesExternalFilterPass` is called once for each row node in the grid. If you return `false`, the node will be excluded from the final set.

> **Note**
>
> If the external filter changes, you need to call `api.onFilterChanged()` to tell the grid.

## Example

The example below shows an external filter in action.

#### External Filter

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  DateFilterModule,
  DoesExternalFilterPass,
  ExternalFilterModule,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IDateFilterParams,
  IRowNode,
  IsExternalFilterPresent,
  ModuleRegistry,
  NumberFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  SetFilterModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  ExternalFilterModule,
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  SetFilterModule,
  NumberFilterModule,
  DateFilterModule,
];

const dateFilterParams: IDateFilterParams = {
  comparator: (filterLocalDateAtMidnight: Date, cellValue: string) => {
    const cellDate = asDate(cellValue);
    if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) {
      return 0;
    }
    if (cellDate < filterLocalDateAtMidnight) {
      return -1;
    }
    if (cellDate > filterLocalDateAtMidnight) {
      return 1;
    }
    return 0;
  },
};

let ageType = "everyone";

function asDate(dateAsString: string) {
  const splitFields = dateAsString.split("/");
  return new Date(
    Number.parseInt(splitFields[2]),
    Number.parseInt(splitFields[1]) - 1,
    Number.parseInt(splitFields[0]),
  );
}

const GridExample = () => {
  const gridRef = useRef<AgGridReact<IOlympicData>>(null);
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<IOlympicData[]>();
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 180 },
    { field: "age", filter: "agNumberColumnFilter", maxWidth: 80 },
    { field: "country" },
    { field: "year", maxWidth: 90 },
    {
      field: "date",
      filter: "agDateColumnFilter",
      filterParams: dateFilterParams,
    },
    { field: "gold", filter: "agNumberColumnFilter" },
    { field: "silver", filter: "agNumberColumnFilter" },
    { field: "bronze", filter: "agNumberColumnFilter" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 120,
      filter: true,
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: IOlympicData[]) => {
        (document.querySelector("#everyone") as HTMLInputElement).checked =
          true;
        setRowData(data);
      });
  }, []);

  const externalFilterChanged = useCallback((newValue: string) => {
    ageType = newValue;
    gridRef.current!.api.onFilterChanged();
  }, []);

  const isExternalFilterPresent = useCallback((): boolean => {
    // if ageType is not everyone, then we are filtering
    return ageType !== "everyone";
  }, []);

  const doesExternalFilterPass = useCallback(
    (node: IRowNode<IOlympicData>): boolean => {
      if (node.data) {
        switch (ageType) {
          case "below25":
            return node.data.age < 25;
          case "between25and50":
            return node.data.age >= 25 && node.data.age <= 50;
          case "above50":
            return node.data.age > 50;
          case "dateAfter2008":
            return asDate(node.data.date) > new Date(2008, 0, 1);
          default:
            return true;
        }
      }
      return true;
    },
    [ageType],
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="test-container">
          <div className="test-header">
            <label>
              <input
                type="radio"
                name="filter"
                id="everyone"
                onChange={() => externalFilterChanged("everyone")}
              />
              Everyone
            </label>
            <label>
              <input
                type="radio"
                name="filter"
                id="below25"
                onChange={() => externalFilterChanged("below25")}
              />
              Below 25
            </label>
            <label>
              <input
                type="radio"
                name="filter"
                id="between25and50"
                onChange={() => externalFilterChanged("between25and50")}
              />
              Between 25 and 50
            </label>
            <label>
              <input
                type="radio"
                name="filter"
                id="above50"
                onChange={() => externalFilterChanged("above50")}
              />
              Above 50
            </label>
            <label>
              <input
                type="radio"
                name="filter"
                id="dateAfter2008"
                onChange={() => externalFilterChanged("dateAfter2008")}
              />
              After 01/01/2008
            </label>
          </div>

          <div style={gridStyle}>
            <AgGridReact<IOlympicData>
              ref={gridRef}
              rowData={rowData}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              isExternalFilterPresent={isExternalFilterPresent}
              doesExternalFilterPass={doesExternalFilterPass}
              onGridReady={onGridReady}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: External Filter](https://www.ag-grid.com/examples/filter-external/external-filter/reactFunctionalTs)
