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_configurationwithin a dashboard'sjson_metadata, as defined insuperset/models/dashboard.py. - Cascading dependencies are declared via the
cascadeParentIdsarray, which lists the UUIDs of parent filters that must have values before the child filter activates. - Client-side enforcement occurs through the
useFilterDependencieshook inuseFilterDependencies.ts, which selects parent filters from the Redux store and determines if the current filter should be disabled. - Value reset logic in
FilterValue.tsxautomatically clears dependent filter selections when parent values change, preventing stale data. - UI configuration is handled by
FiltersConfigForm.tsx, which persists thecascadeParentIdsarray 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →