---
title: "Set Filter - Tree List"
enterprise: true
framework: react
version: "36.1.0"
---

# Set Filter - Tree List

This section describes the behaviour of the Set Filter Tree List and shows how it can be configured.

The Tree List allows the user to display the values in the Filter List grouped in a tree structure.

![Filter Tree List](https://www.ag-grid.com/_astro/set-filter-tree-list.BVJPEUdY.png)

## Enabling Tree Lists

Tree List is enabled by setting `filterParams.treeList = true`. There are four different ways the tree structure can be created:

- The column values are of type `Date`, in which case the tree will be year -> month -> day. Note that if the `Date` objects have a time defined, then a Key Creator must also be supplied to generate a unique key without the time.
- Tree Data mode is enabled and the column is a group column. The Filter List will match the tree structure. A Key Creator must be supplied to convert the array of keys.
- Grouping is enabled and the column is the group column. The Filter List will match the group structure. A Key Creator must be supplied to convert the array of keys.
- A `filterParams.treeListPathGetter` is provided to get a custom tree path for the column values. Each row must map to a leaf value in the tree. If the column values are [Complex Objects](https://www.ag-grid.com/react-data-grid/filter-set-filter-list/#complex-objects), a Key Creator will also be required.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `keyCreator` | `Function` |  |  | Function to return a string key for a value. This is required when the filter values are complex objects, or when `treeList = true` and the column is a group column with Tree Data or Grouping enabled. If not provided, the Column Definition Key Creator is used. |
| `treeListPathGetter` | `Function` |  |  | Requires `treeList = true`. If provided, this gets the tree path to display in the Set Filter List based on the column values. Each row must map to a leaf value in the tree. |

```jsx
const [columnDefs, setColumnDefs] = useState([
    {
        field: 'date',
        filter: 'agSetColumnFilter',
        filterParams: {
            treeList: true,
        }
    }
]);
const autoGroupColumnDef = useMemo(() => { 
	return {
        field: 'athlete',
        filter: 'agSetColumnFilter',
        filterParams: {
            treeList: true,
            keyCreator: params => params.value.join('#')
        },
    };
}, []);

<AgGridReact
    columnDefs={columnDefs}
    autoGroupColumnDef={autoGroupColumnDef}
/>
```

The following example demonstrates enabling different types of Tree List in the Set Filter. Note the following:

- The **Group**, **Date** and **Gold** columns all have `filterParams.treeList = true`.
- The **Group** column Filter List matches the format of the Row Grouping. A Key Creator is specified to convert the path into a string.
- The **Date** column is grouped by year -> month -> day.
- The **Gold** column has `filterParams.treeListPathGetter` provided which groups the values into a tree of >2 and <=2.

#### Filter Tree List

```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 {
  AutoGroupColumnDef,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ISetFilterParams,
  KeyCreatorParams,
  ModuleRegistry,
  NumberFilterModule,
  ValueFormatterParams,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  FiltersToolPanelModule,
  RowGroupingModule,
  SetFilterModule,
} from "ag-grid-enterprise";

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

const modules = [
  NumberFilterModule,
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  FiltersToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  SetFilterModule,
  RowGroupingModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<any[]>();
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "country", rowGroup: true, hide: true },
    { field: "sport", rowGroup: true, hide: true },
    { field: "athlete", hide: true },
    {
      field: "date",
      filter: "agSetColumnFilter",
      filterParams: {
        treeList: true,
      } as ISetFilterParams<any, Date>,
    },
    {
      field: "gold",
      filter: "agSetColumnFilter",
      filterParams: {
        treeList: true,
        treeListPathGetter: (gold: number) =>
          gold != null ? [gold > 2 ? ">2" : "<=2", String(gold)] : [null],
      } as ISetFilterParams<any, number>,
    },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 200,
      floatingFilter: true,
      cellDataType: false,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      field: "athlete",
      filter: "agSetColumnFilter",
      filterParams: {
        treeList: true,
        keyCreator: (params: KeyCreatorParams) =>
          params.value ? params.value.join("#") : null,
      } as ISetFilterParams,
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: any[]) =>
        setRowData(
          data.map((row) => {
            const dateParts = row.date.split("/");
            const newDate = new Date(
              parseInt(dateParts[2]),
              dateParts[1] - 1,
              dateParts[0],
            );
            return { ...row, date: newDate };
          }),
        ),
      );
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact
            rowData={rowData}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            autoGroupColumnDef={autoGroupColumnDef}
            onGridReady={onGridReady}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

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

[Live example: Filter Tree List](https://www.ag-grid.com/examples/filter-set-tree-list/filter-tree-list/reactFunctionalTs)

> **Note**
>
> If using [Cell Data Types](https://www.ag-grid.com/react-data-grid/cell-data-types/), Tree List is automatically enabled for columns containing date values.

## Sorting Tree Lists

Sorting values for Tree Lists is similar to [Sorting Filter Lists](https://www.ag-grid.com/react-data-grid/filter-set-filter-list/#sorting-filter-lists), with the exception that if the column values are of type `Date`, they will instead be sorted based on the raw date values.

A Comparator can be used to change the sort order of Tree Lists just like with [Sorting Filter Lists](https://www.ag-grid.com/react-data-grid/filter-set-filter-list/#sorting-filter-lists), with the same conditions applying. For Tree Lists, the Comparator is applied to the child values, sorting the entire tree in one pass rather than for each level. The Comparator will be provided the following:

- The column value for `Date` objects and custom tree paths.
- The tree path (`(string | null)[]` or `null`) for Tree Data and Grouping.

The following example demonstrates changing the sorting of the Tree List. Note the following:

- Tree Data is turned on via `treeData = true`.
- The **Employee** column has `filterParams.treeList = true` and the Filter List matches the format of the Tree Data. A Key Creator is specified to convert the path into a string.
- The **Employee** column has a `filterParams.comparator` supplied which displays the Filter List in reverse alphabetical order.
- The **Start Date** column has `filterParams.treeList = true`. It also has a `filterParams.comparator` supplied which displays the Filter List in reverse date order.

#### Sorting Tree Lists

```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 {
  AutoGroupColumnDef,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GetDataPath,
  GetRowIdFunc,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ISetFilterParams,
  KeyCreatorParams,
  ModuleRegistry,
  ValueFormatterParams,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  FiltersToolPanelModule,
  SetFilterModule,
  TreeDataModule,
} from "ag-grid-enterprise";

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

const modules = [
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  FiltersToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  SetFilterModule,
  TreeDataModule,
];

const arrayComparator: (a: string[] | null, b: string[] | null) => number = (
  a: string[] | null,
  b: string[] | null,
) => {
  if (a == null) {
    return b == null ? 0 : -1;
  } else if (b == null) {
    return 1;
  }
  for (let i = 0; i < a.length; i++) {
    if (i >= b.length) {
      return 1;
    }
    const comparisonValue = reverseOrderComparator(a[i], b[i]);
    if (comparisonValue !== 0) {
      return comparisonValue;
    }
  }
  return 0;
};

const reverseOrderComparator: (a: any, b: any) => number = (a: any, b: any) => {
  return a < b ? 1 : a > b ? -1 : 0;
};

function processData(data: any[]) {
  const flattenedData: any[] = [];
  const flattenRowRecursive = (row: any, parentPath: string[]) => {
    const dateParts = row.startDate.split("/");
    const startDate = new Date(
      parseInt(dateParts[2]),
      dateParts[1] - 1,
      dateParts[0],
    );
    const dataPath = [...parentPath, row.employeeName];
    flattenedData.push({ ...row, dataPath, startDate });
    if (row.underlings) {
      row.underlings.forEach((underling: any) =>
        flattenRowRecursive(underling, dataPath),
      );
    }
  };
  data.forEach((row) => flattenRowRecursive(row, []));
  return flattenedData;
}

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<any[]>();
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "employmentType" },
    {
      field: "startDate",
      valueFormatter: (params) =>
        params.value ? params.value.toLocaleDateString() : params.value,
      filterParams: {
        treeList: true,
        comparator: reverseOrderComparator,
      } as ISetFilterParams<any, Date>,
    },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 200,
      filter: true,
      floatingFilter: true,
      cellDataType: false,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      headerName: "Employee",
      field: "employeeName",
      cellRendererParams: {
        suppressCount: true,
      },
      filter: "agSetColumnFilter",
      filterParams: {
        treeList: true,
        keyCreator: (params: KeyCreatorParams) =>
          params.value ? params.value.join("#") : null,
        comparator: arrayComparator,
      } as ISetFilterParams<any, string[]>,
      minWidth: 280,
    };
  }, []);
  const getDataPath = useCallback((data) => {
    return data.dataPath;
  }, []);
  const getRowId = useCallback((params) => String(params.data.employeeId), []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/tree-data.json")
      .then((resp) => resp.json())
      .then((data: any[]) => setRowData(processData(data)));
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact
            rowData={rowData}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            autoGroupColumnDef={autoGroupColumnDef}
            treeData={true}
            groupDefaultExpanded={-1}
            getDataPath={getDataPath}
            getRowId={getRowId}
            onGridReady={onGridReady}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

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

[Live example: Sorting Tree Lists](https://www.ag-grid.com/examples/filter-set-tree-list/sorting-tree-lists/reactFunctionalTs)

## Formatting Values

The values can be formatted in the Filter List via `filterParams.treeListFormatter`. This allows a different format to be used for each level of the tree.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `treeListFormatter` | `Function` |  |  | Requires `treeList = true`. If specified, this formats the tree values before they are displayed in the Filter List. `pathKey` - The key for the current node in the tree. `level` - The level of the current node in the tree (starting at 0). `parentPathKeys` - The keys of the parent nodes up until the current node (exclusive). This will be an empty array if the node is at the root level. |

```jsx
const [columnDefs, setColumnDefs] = useState([
    {
        field: 'date',
        filter: 'agSetColumnFilter',
        filterParams: {
            treeList: true,
            treeListFormatter: (pathKey, level, parentPathKeys) => {
                if (level === 0 && pathKey) {
                    return `Year ${pathKey}`;
                }
                return pathKey;
            }
        }
    }
]);

<AgGridReact columnDefs={columnDefs} />
```

If a formatter is provided, it will also need to handle [Missing Values](https://www.ag-grid.com/react-data-grid/filter-set-filter-list/#missing-values), which will have a `pathKey` of `null`. Without a formatter, these are displayed as `(Blanks)`.

`filterParams.valueFormatter` is not used in the Filter List when `filterParams.treeList = true`. However, it is still used to format the values displayed in the Floating Filter. The value provided to the Value Formatter is the original value, e.g. a `Date` object for dates, the path for Tree Data or Grouping, or the column value for a custom tree path.

The following example demonstrates formatting the Tree List. Note the following:

- The **Group** column has `filterParams.treeList = true`.
- The **Group** column has a `filterParams.treeListFormatter` provided which formats the country values in the Filter List to add a two letter country code. Missing values are formatted as `(Blanks)`.
- The **Date** column has `filterParams.treeList = true`.
- The **Date** column has a `filterParams.treeListFormatter` provided which formats the numerical month value to display as the name of the month. Missing values are formatted as `(Blanks)`.
- When a date is filtered in the **Date** column , `filterParams.valueFormatter` is used to format the value displayed in the Floating Filter.

#### Formatting Tree List Values

```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 {
  AutoGroupColumnDef,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ISetFilterParams,
  KeyCreatorParams,
  ModuleRegistry,
  NumberFilterModule,
  ValueFormatterParams,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  FiltersToolPanelModule,
  RowGroupingModule,
  SetFilterModule,
} from "ag-grid-enterprise";

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

const modules = [
  NumberFilterModule,
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  FiltersToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  SetFilterModule,
  RowGroupingModule,
];

function dateCellValueFormatter(params: ValueFormatterParams) {
  return params.value ? params.value.toLocaleDateString() : "";
}

function dateFloatingFilterValueFormatter(params: ValueFormatterParams) {
  return params.value ? params.value.toLocaleDateString() : "(Blanks)";
}

function treeListFormatter(
  pathKey: string | null,
  level: number,
  _parentPathKeys: (string | null)[],
): string {
  if (level === 1) {
    const date = new Date();
    date.setMonth(Number(pathKey) - 1);
    return date.toLocaleDateString(undefined, { month: "long" });
  }
  return pathKey || "(Blanks)";
}

function groupTreeListFormatter(
  pathKey: string | null,
  level: number,
  _parentPathKeys: (string | null)[],
): string {
  if (level === 0 && pathKey) {
    return pathKey + " (" + pathKey.substring(0, 2).toUpperCase() + ")";
  }
  return pathKey || "(Blanks)";
}

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<any[]>();
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "country", rowGroup: true, hide: true },
    { field: "sport" },
    {
      field: "date",
      valueFormatter: dateCellValueFormatter,
      filter: "agSetColumnFilter",
      filterParams: {
        treeList: true,
        treeListFormatter: treeListFormatter,
        valueFormatter: dateFloatingFilterValueFormatter,
      } as ISetFilterParams<any, Date>,
    },
    {
      field: "gold",
    },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      floatingFilter: true,
      cellDataType: false,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      field: "athlete",
      filter: "agSetColumnFilter",
      filterParams: {
        treeList: true,
        keyCreator: (params: KeyCreatorParams) =>
          params.value ? params.value.join("#") : null,
        treeListFormatter: groupTreeListFormatter,
      } as ISetFilterParams,
      minWidth: 200,
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: any[]) => {
        const randomDays = [1, 4, 10, 15, 18];
        setRowData([
          {},
          ...data.map((row) => {
            // generate pseudo-random dates
            const dateParts = row.date.split("/");
            const randomMonth =
              parseInt(dateParts[1]) - Math.floor(window.agRandom() * 3);
            const newDate = new Date(
              parseInt(dateParts[2]),
              randomMonth,
              randomMonth + randomDays[Math.floor(window.agRandom() * 5)],
            );
            return { ...row, date: newDate };
          }),
        ]);
      });
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact
            rowData={rowData}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            autoGroupColumnDef={autoGroupColumnDef}
            onGridReady={onGridReady}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

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

[Live example: Formatting Tree List Values](https://www.ag-grid.com/examples/filter-set-tree-list/formatting-tree-list-values/reactFunctionalTs)

## Complex Objects

[Complex Objects](https://www.ag-grid.com/react-data-grid/filter-set-filter-list/#complex-objects) can be used with Tree List in the same way as with a normal Set Filter List.

The following example demonstrates complex objects being used with Tree Data and the Tree List. Note the following:

- Tree Data is turned on via `treeData = true`.
- The **Employee** column contains complex objects and has `filterParams.treeList = true`.
- The data path returned by `getDataPath` does not have unique IDs at each level. The **Employee** column has a `treeListFormatter` defined which uses the parent path keys to get the full route to the node, so that the correct value can be displayed in the Tree List.

#### Tree List Complex Objects

```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 {
  AutoGroupColumnDef,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GetDataPath,
  GridApi,
  GridOptions,
  ISetFilterParams,
  KeyCreatorParams,
  ModuleRegistry,
  ValueFormatterParams,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  FiltersToolPanelModule,
  SetFilterModule,
  TreeDataModule,
} from "ag-grid-enterprise";
import { getData } from "./data";

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

const modules = [
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  FiltersToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  SetFilterModule,
  TreeDataModule,
];

const pathLookup: {
  [key: string]: string;
} = getData().reduce((pathMap, row) => {
  pathMap[row.path.key] = row.path.displayValue;
  return pathMap;
}, {});

function treeListFormatter(
  pathKey: string | null,
  _level: number,
  parentPathKeys: (string | null)[],
): string {
  return pathLookup[[...parentPathKeys, pathKey].join(".")];
}

const valueFormatter: (params: ValueFormatterParams) => string = (
  params: ValueFormatterParams,
) => {
  return params.value ? pathLookup[params.value.join(".")] : "(Blanks)";
};

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<any[]>(getData());
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "employmentType" },
    { field: "jobTitle" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 200,
      filter: true,
      floatingFilter: true,
      cellDataType: false,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      headerName: "Employee",
      field: "path",
      cellRendererParams: {
        suppressCount: true,
      },
      filter: "agSetColumnFilter",
      filterParams: {
        treeList: true,
        keyCreator: (params: KeyCreatorParams) => params.value.join("."),
        treeListFormatter: treeListFormatter,
        valueFormatter: valueFormatter,
      } as ISetFilterParams<any, string[]>,
      minWidth: 280,
      valueFormatter: (params: ValueFormatterParams) =>
        params.value.displayValue,
    };
  }, []);
  const getDataPath = useCallback((data) => data.path.key.split("."), []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact
            rowData={rowData}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            autoGroupColumnDef={autoGroupColumnDef}
            treeData={true}
            groupDefaultExpanded={-1}
            getDataPath={getDataPath}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

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

[Live example: Tree List Complex Objects](https://www.ag-grid.com/examples/filter-set-tree-list/tree-list-complex-objects/reactFunctionalTs)

## Mini Filter Behaviour

When searching in the Mini Filter, all children will be included when a parent matches the search value. A parent will be included if it has any children that match the search value, or it matches itself.

## Filter Value Tooltips

When using Tree List with a [Custom Tooltip Component](https://www.ag-grid.com/react-data-grid/tooltips/), the tooltip params will be of type `ISetFilterTreeListTooltipParams` which extends the Custom Tooltip params to include the level of the item within the tree.

Additional property available on `ISetFilterTreeListTooltipParams`:

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `level` | `number` |  |  | Level of the tree (starting at 0). |
