---
product: "AG Studio"
title: "Filters Panel"
description: "Use AG Studio's Filters Panel to add filters, review the filter cards on a page, apply held changes, and see conditions set by Filter Widgets."
framework: react
version: "3.1.0"
related:
    - title: "Page and Widget Filters"
      url: "https://www.ag-grid.com/studio/analysts/page-and-widget-filters/"
    - title: "Filter Types"
      url: "https://www.ag-grid.com/studio/analysts/filter-types/"
    - title: "Cross-Filtering"
      url: "https://www.ag-grid.com/studio/analysts/cross-filtering/"
llms: "https://www.ag-grid.com/studio/llms.txt"
---

# Filters Panel

Filters change the data shown on a Page or Widget.

There are four ways to filter in AG Studio:

- **Page Filters** apply to all Widgets on the page. See [Page and Widget Filters](https://www.ag-grid.com/studio/analysts/page-and-widget-filters/).
- **Widget Filters** apply to one Widget. See [Page and Widget Filters](https://www.ag-grid.com/studio/analysts/page-and-widget-filters/).
- **Cross-filtering** filters other Widgets when you click a value in a Widget. See [Cross-Filtering](https://www.ag-grid.com/studio/analysts/cross-filtering/).
- **Filter Widgets** are filter controls on the page itself. See [Filter Widgets](https://www.ag-grid.com/studio/analysts/filter-widgets/).

Page and Widget Filters use the types described in [Filter Types](https://www.ag-grid.com/studio/analysts/filter-types/). Whichever way you filter, the conditions appear in the Filters Panel, which this page describes.

## Filter Cards

The Filters Panel is where you manage every kind of filter. Each filter appears as a card, and the cards are organised by type.

In [Edit Mode](https://www.ag-grid.com/studio/analysts/user-interface/#modes), you can add and remove filters, and reorder them by dragging their cards within a section. In [View Mode](https://www.ag-grid.com/studio/analysts/user-interface/#modes), you can change the settings of Page Filters and Widget Filters, but by default you cannot add or remove filters. Your developer can enable this. In either mode, you can remove read-only cards, such as those for Filter Widgets and cross-filtering conditions, from the Filters Panel. The exception is drill-through filters, which cannot be removed this way.

Filter Widgets and cross-filtering conditions appear as read-only, collapsed cards, so you can see at a glance what is affecting the data shown. Clicking a Widget Filter or Cross Filter card puts the Widget it came from into focus.

## Adding a Filter

The Page Filters section ends with an **Add Filter** button. Click it to open a list of fields, type in the search box to narrow the list, and pick a field. You can also drag a field from the [Data Panel](https://www.ag-grid.com/studio/analysts/user-interface/#data-panel) and drop it on Add Filter. The new filter card opens expanded so you can set its condition.

The Widget Filters section appears only while a Widget is selected, and has its own Add Filter button. To add a Widget Filter, select the Widget first.

A new filter starts as a [Simple Filter](https://www.ag-grid.com/studio/analysts/filter-types/#simple-filter). Its first operator is Contains for a text field, Equals for a numeric field, and On for a Date or DateTime field. For a true/false field it is Choose one, and you then pick Is true, Is false, Blank or Not blank. If the field supports more than one filter type, the card shows a drop-down where you can change the type.

## Held Filter Changes

By default, a change to a filter card takes effect straight away and the page updates as you type.

Your developer can instead set up the Filters Panel to hold changes until you confirm them. Each expanded filter card then shows **Apply** and **Cancel** buttons at the bottom. Nothing on the page changes until you press Apply, and Cancel returns the card to the values that were last applied. This suits reports that fetch their data from a server, where every keystroke would otherwise start a new query.

A card holding changes you have not applied yet is marked with an edit indicator in its header, so you can still see which filters are pending while the cards are collapsed. A collapsed card always describes the filter that is actually applied, not the change you are part-way through making.

A filter that is only partly filled in, such as the start of a range with no end, does not count as a filter at all, so Apply stays greyed out until the condition is complete. Likewise, clearing one end of a range that has already been applied removes the filter, because the remaining condition is incomplete.

The example below is in View Mode and opens with a region Page Filter and a date Page Filter already applied, with both cards expanded so you can see the buttons. The Filters Panel is set up to hold changes, so changing a card's values does not apply them straight away. Try adding or removing a region: the Widgets stay as they are until you press Apply, and Cancel restores the values that were last applied.

#### Staged Filter Changes

```ts
import {
  AgReportState,
  AgStudioApi,
  AgStudioProperties,
  createStudio,
  enableStudioDevValidations,
} from "ag-studio";
import { getMainDemoData } from "./data.ts";

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

const initialState: AgReportState = {
  pages: [
    {
      id: "a",
      widgets: {
        "kpi-net-sales": {
          type: "value",
          dataMapping: { value: [{ id: "net_sales" }] },
          format: { style: { label: { enabled: true, text: "Net Sales" } } },
        },
        "kpi-order-count": {
          type: "value",
          dataMapping: { value: [{ id: "order_count" }] },
          format: { style: { label: { enabled: true, text: "Order Count" } } },
        },
        "kpi-aov": {
          type: "value",
          dataMapping: { value: [{ id: "average_order_value" }] },
          format: {
            style: { label: { enabled: true, text: "Avg Order Value" } },
          },
        },
        "net-sales-by-region": {
          type: "bar-chart-grouped",
          dataMapping: {
            categoryKey: [{ id: "stores.region" }],
            valueKey: [{ id: "net_sales" }],
          },
          format: { title: { enabled: true, text: "Net Sales by Region" } },
        },
        "net-sales-by-subcategory": {
          type: "bar-chart-grouped",
          dataMapping: {
            categoryKey: [{ id: "products.subcategory" }],
            valueKey: [{ id: "net_sales" }],
          },
          format: {
            title: { enabled: true, text: "Net Sales by Subcategory" },
          },
        },
      },
      widgetLayout: {
        "kpi-net-sales": { xTrack: 0, yTrack: 0, xSpan: 8, ySpan: 6 },
        "kpi-order-count": { xTrack: 8, yTrack: 0, xSpan: 8, ySpan: 6 },
        "kpi-aov": { xTrack: 16, yTrack: 0, xSpan: 8, ySpan: 6 },
        "net-sales-by-region": { xTrack: 0, yTrack: 6, xSpan: 24, ySpan: 18 },
        "net-sales-by-subcategory": {
          xTrack: 0,
          yTrack: 24,
          xSpan: 24,
          ySpan: 18,
        },
      },
      filter: {
        // Both cards start expanded and already applied, so the Apply and Cancel buttons
        // are visible on load without the reader having to add a filter first.
        page: [
          {
            field: { id: "stores.region" },
            view: { expanded: true, viewTypeId: "selection" },
            model: {
              operator: "isIn",
              value: ["DACH", "UK"],
            },
          },
          {
            field: { id: "orders.order_datetime" },
            view: { expanded: true },
            model: {
              operator: "greaterThan",
              value: "2024-12-01",
            },
          },
        ],
      },
    },
  ],
  selectedPageId: "a",
};

const studioProperties: AgStudioProperties = {
  mode: "view",
  initialState,
  panels: {
    // An options-only config leaves each mode's default panels in place, so view mode still
    // shows the filters panel on the right.
    options: {
      filters: { applyOnChange: false },
    },
  },
  data: getMainDemoData("https://www.ag-grid.com/studio/example-assets"),
};

let studioApi: AgStudioApi;

const studioDiv = document.querySelector<HTMLElement>("#myStudio")!;
studioApi = createStudio(studioDiv, studioProperties);
```

[Live example: Staged Filter Changes](https://www.ag-grid.com/studio/examples/filters-panel/staged-filters/typescript/)

## Filter Widget Cards

[Filter Widgets](https://www.ag-grid.com/studio/analysts/filter-widgets/) let you filter the page with controls on the page itself, without opening the Filters Panel. When a Filter Widget applies a condition, a read-only card appears in the Page Filters section. Clearing that card with its 'x' action is the same as clearing the Filter Widget's selection.

Filter Widgets do not update in response to Page and Widget Filters or cross-filter conditions. In [Edit Mode](https://www.ag-grid.com/studio/analysts/user-interface/#modes), clicking a Filter Widget puts it in focus and shows its options in the Filters Panel, where a Widget Filter can limit the values it offers.

The example below uses List Filter Widgets to filter a nuclear dataset. Click values in any filter list to apply a condition, and watch its card appear in the Filters Panel. Then switch to Edit Mode, click a Filter Widget to put it in focus, and change its options in the Filters Panel to customise the selections it offers.

#### Filter Widgets

```ts
import {
  AgReportState,
  AgStudioApi,
  AgStudioProperties,
  createStudio,
  enableStudioDevValidations,
} from "ag-studio";
import { getNuclearDataSource } from "./data.ts";

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

const initialState: AgReportState = {
  pages: [
    {
      id: "segre-chart",
      widgets: {
        "report-title": {
          type: "text",
          dataMapping: {},
          format: {
            style: {
              text: "Nuclear Wallet Cards - Segrè Chart Visualization",
              typography: { fontWeight: "normal" },
            },
          },
        },
        "element-family-filter": {
          type: "list-filter",
          dataMapping: {
            value: [{ id: "nuclear-data.Element Family" }],
          },
        },
        "stability-filter": {
          type: "list-filter",
          dataMapping: {
            value: [{ id: "nuclear-data.Stability" }],
          },
        },
        "occurrence-filter": {
          type: "list-filter",
          dataMapping: {
            value: [{ id: "nuclear-data.Occurrence" }],
          },
        },
        "decay-mode-filter": {
          type: "list-filter",
          dataMapping: {
            value: [{ id: "nuclear-data.Decay Mode" }],
          },
        },
        "nuclides-chart": {
          type: "scatter-chart",
          dataMapping: {
            categoryKey: [
              { id: "nuclear-data.Neutrons (N)", aggregation: "first" },
            ],
            valueKey: [
              { id: "nuclear-data.Atomic Number (Z)", aggregation: "first" },
            ],
            groupByKey: [{ id: "nuclear-data.Element + Atomic Mass (A)" }],
            tooltipKey: [
              { id: "nuclear-data.Element", aggregation: "first" },
              { id: "nuclear-data.Atomic Mass (A)", aggregation: "first" },
            ],
          },
          format: {
            title: {
              enabled: true,
              text: "Chart of Nuclides",
            },
            subtitle: {
              enabled: true,
              text: "A Segrè chart showing isotopes by neutron count and atomic number.",
            },
          },
        },
        "element-grid": {
          type: "grid",
          dataMapping: {
            cols: [
              { id: "nuclear-data.Element + Atomic Mass (A)" },
              { id: "nuclear-data.Element" },
              { id: "nuclear-data.Atomic Number (Z)" },
              { id: "nuclear-data.Atomic Mass (A)" },
              { id: "nuclear-data.Neutrons (N)" },
              { id: "nuclear-data.Half-Life" },
              { id: "nuclear-data.Half-Life (Unit)" },
              { id: "nuclear-data.Spin-Parity" },
              { id: "nuclear-data.Decay Modes" },
            ],
          },
          format: {
            title: {
              enabled: true,
              text: "Nuclear Properties",
            },
            style: {
              theme: { rowHeight: 28 },
            },
          },
        },
      },
      widgetLayout: {
        "report-title": { xTrack: 0, yTrack: 0, xSpan: 24, ySpan: 4 },
        "element-family-filter": { xTrack: 0, yTrack: 4, xSpan: 6, ySpan: 12 },
        "stability-filter": { xTrack: 0, yTrack: 16, xSpan: 6, ySpan: 9 },
        "occurrence-filter": { xTrack: 0, yTrack: 25, xSpan: 6, ySpan: 6 },
        "decay-mode-filter": { xTrack: 0, yTrack: 31, xSpan: 6, ySpan: 9 },
        "nuclides-chart": { xTrack: 6, yTrack: 4, xSpan: 18, ySpan: 36 },
        "element-grid": { xTrack: 0, yTrack: 40, xSpan: 24, ySpan: 14 },
      },
      filter: {
        page: [],
      },
    },
  ],
  selectedPageId: "segre-chart",
};

let studioApi: AgStudioApi;

async function getStudioProperties(): Promise<AgStudioProperties> {
  const nuclearSource = await getNuclearDataSource();

  return {
    mode: "view",
    initialState,
    data: {
      sources: [nuclearSource],
      relationships: [],
    },
  };
}

const studioDiv = document.querySelector<HTMLElement>("#myStudio")!;
const studioProperties = await getStudioProperties();
studioApi = createStudio(studioDiv, studioProperties);
```

[Live example: Filter Widgets](https://www.ag-grid.com/studio/examples/filters-panel/filter-widgets/typescript/)
