How to Implement Native Filters and Dependent/Cascading Filters in Apache Superset Dashboards

Native filters in Apache Superset are configured via the native_filter_configuration JSON array in dashboard metadata, where dependent filters declare parent relationships through the cascadeParentIds property, enabling automatic client-side and server-side cascading behavior.

Apache Superset’s native filter system allows dashboard builders to create interactive, cascading filter experiences without writing custom SQL. By leveraging the cascadeParentIds configuration and the built-in React state management, you can build dependent filters that automatically adjust their available options based on parent selections.

Understanding the Native Filter Architecture

Backend Storage and Validation

The dashboard model stores native filter configurations inside the json_metadata column. When a dashboard is saved or loaded, Superset validates the native_filter_configuration array.

In superset/models/dashboard.py (lines 389-395), the dashboard reads the native filter configuration from the JSON metadata:


# From superset/models/dashboard.py

native_filter_configuration = json_metadata.get("native_filter_configuration", [])

The Data Access Object (DAO) layer exposes this configuration via REST endpoints. In superset/daos/dashboard.py (lines 339-350), the get_native_filter_configuration method returns the raw configuration for API consumers:


# From superset/daos/dashboard.py

def get_native_filter_configuration(self, dashboard_id: int) -> list[dict]:
    dashboard = self.get_by_id(dashboard_id)
    return dashboard.json_metadata.get("native_filter_configuration", [])

Frontend State Management

On the frontend, Superset maintains a normalized Redux store for native filters. The state slice defined in superset-frontend/src/dashboard/components/nativeFilters/state.ts stores each filter keyed by its UUID, including its current value, default value, and configuration.

This centralized state enables any component to access parent filter values when evaluating dependencies.

Implementing Cascading Dependencies

Declaring Parent-Child Relationships

Cascading behavior is declared in the filter configuration object through the cascadeParentIds array. This array contains the UUIDs of filters that must have values selected before the dependent filter becomes active.

A typical configuration looks like this:

{
  "native_filter_configuration": [
    {
      "id": "NATIVE_FILTER-1",
      "name": "Country",
      "type": "filter_select",
      "targets": [{ "datasetId": 42, "column": { "name": "country" } }],
      "defaultDataMask": { "filterState": { "value": [] } }
    },
    {
      "id": "NATIVE_FILTER-2",
      "name": "City",
      "type": "filter_select",
      "cascadeParentIds": ["NATIVE_FILTER-1"],
      "targets": [{ "datasetId": 42, "column": { "name": "city" } }],
      "defaultDataMask": { "filterState": { "value": [] } }
    }
  ]
}

In this example, the City filter declares Country as its parent via cascadeParentIds. Until a user selects a country, the City filter remains disabled or hidden.

Client-Side Dependency Resolution

The frontend uses a dedicated React hook to resolve parent-child relationships. In superset-frontend/src/dashboard/components/nativeFilters/FilterCard/useFilterDependencies.ts (lines 41-49), the useFilterDependencies hook reads the cascadeParentIds from a filter's configuration and selects the corresponding parent filter objects from the Redux store:

// From useFilterDependencies.ts
const useFilterDependencies = (filter: Filter) => {
  const parentIds = filter.cascadeParentIds ?? [];
  const parentFilters = useSelector((state: State) =>
    parentIds.map(id => state.nativeFilters.filters[id]),
  );
  return parentFilters;
};

This hook enables UI components to determine if a filter should be disabled based on whether all parent filters have selected values.

Automatic Value Reset on Parent Change

When a parent filter value changes, Superset automatically resets dependent filter values to prevent stale selections. This logic resides in superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx (lines 184-190):

// From FilterValue.tsx
if ((filter.cascadeParentIds ?? []).length) {
  (filter.cascadeParentIds ?? []).forEach(parentId => {
    const parent = filters[parentId];
    // Logic that clears dependent filter if parent value becomes empty
    if (!parent?.defaultDataMask?.filterState?.value?.length) {
      // Reset current filter value
    }
  });
}

This ensures data consistency: if a user clears the Country selection, the City filter automatically clears its value and becomes disabled until a new country is chosen.

Configuration via the UI

Setting Dependencies in the Filter Modal

Dashboard editors configure cascading relationships through the Filters Config Modal. In superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx (lines 325-327), the form stores the selected parent IDs in the cascadeParentIds array:

// From FiltersConfigForm.tsx
const dependencies = formFilter?.dependencies || filterToEdit?.cascadeParentIds || [];

When a user selects "Depends on" in the UI, the modal populates this array and persists it to the dashboard's json_metadata upon saving.

Practical Implementation Example

Below is a complete example showing both the JSON configuration and a custom React component that respects cascading dependencies.

Dashboard JSON Configuration

{
  "native_filter_configuration": [
    {
      "id": "NATIVE_FILTER-REGION",
      "name": "Sales Region",
      "type": "filter_select",
      "targets": [{ "datasetId": 15, "column": { "name": "region" } }],
      "defaultDataMask": { "filterState": { "value": [] } }
    },
    {
      "id": "NATIVE_FILTER-TERRITORY",
      "name": "Sales Territory",
      "type": "filter_select",
      "cascadeParentIds": ["NATIVE_FILTER-REGION"],
      "targets": [{ "datasetId": 15, "column": { "name": "territory" } }],
      "defaultDataMask": { "filterState": { "value": [] } }
    }
  ]
}

Custom Filter Component with Dependency Support

import React from 'react';
import { Filter } from '@superset-ui/core';
import { useFilterDependencies } from 'src/dashboard/components/nativeFilters/FilterCard/useFilterDependencies';
import { Select } from '@superset-ui/core';

interface Props {
  filter: Filter;
  onChange: (value: any) => void;
}

export const CascadingSelect: React.FC<Props> = ({ filter, onChange }) => {
  const parentFilters = useFilterDependencies(filter);

  const isDisabled = parentFilters.some(
    p => !(p?.defaultDataMask?.filterState?.value?.length),
  );

  return (
    <Select
      placeholder={filter.name}
      disabled={isDisabled}
      value={filter.defaultDataMask?.filterState?.value ?? []}
      onChange={onChange}
      options={[]} // Options fetched dynamically based on parent selections
    />
  );
};

When this component is rendered inside Superset's FilterCard, it automatically disables the Sales Territory dropdown until a Sales Region is selected, and clears the territory value if the region is cleared.

Summary

  • Native filters are stored in native_filter_configuration within a dashboard's json_metadata, as defined in superset/models/dashboard.py.
  • Cascading dependencies are declared via the cascadeParentIds array, which lists the UUIDs of parent filters that must have values before the child filter activates.
  • Client-side enforcement occurs through the useFilterDependencies hook in useFilterDependencies.ts, which selects parent filters from the Redux store and determines if the current filter should be disabled.
  • Value reset logic in FilterValue.tsx automatically clears dependent filter selections when parent values change, preventing stale data.
  • UI configuration is handled by FiltersConfigForm.tsx, which persists the cascadeParentIds array to the dashboard metadata when users select "Depends on" in the filter modal.

Frequently Asked Questions

What is the difference between native filters and filter boxes in Superset?

Native filters are the modern, recommended approach for dashboard filtering in Apache Superset, replacing the legacy "filter box" visualization type. Native filters are defined in the dashboard's json_metadata under native_filter_configuration and offer superior performance, cascading dependencies via cascadeParentIds, and better integration with the dashboard layout. Filter boxes are deprecated visualizations that require adding a special chart to the dashboard and do not support the modern dependency system.

How does Superset handle cascading filter queries on the backend?

When a dashboard loads or updates, the backend receives the current state of all native filters, including their dependencies. The SQL query generation layer uses the cascadeParentIds configuration to ensure that dependent filters only query data filtered by parent selections. The Dashboard model in superset/models/dashboard.py validates that all cascadeParentIds reference existing filter IDs when the dashboard is saved, ensuring referential integrity between parent and child filters.

Can a filter have multiple parent dependencies?

Yes, the cascadeParentIds property is an array that supports multiple parent filter IDs. A dependent filter will only become enabled when all specified parent filters have selected values. The useFilterDependencies hook in useFilterDependencies.ts iterates through all parent IDs and checks that each has a non-empty filterState.value before allowing the child filter to activate. This design supports complex filtering scenarios where a selection depends on multiple upstream criteria.

Where is the cascade configuration stored in the database?

The cascading relationship configuration is stored in the json_metadata column of the dashboards table in the Superset metadata database. Specifically, it resides within the native_filter_configuration array as the cascadeParentIds field on individual filter objects. When a dashboard is saved via the REST API (PUT /api/v1/dashboard/<id>), the Dashboard model in superset/models/dashboard.py validates and persists this JSON structure, ensuring that cascadeParentIds reference valid filter IDs within the same configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →