Native Filters#
Native Filters is Superset's first-class dashboard filtering system. Filters are configured per-dashboard, stored in json_metadata under native_filter_configuration, and rendered in either a vertical sidebar or a horizontal header bar. At runtime, each filter's selected value is tracked as a dataMask object in Redux and applied to charts via formData injection.
Key Entry Points#
| Path | Role |
|---|---|
FilterBar/index.tsx | Root FilterBar component — owns filter state lifecycle, dispatches to Redux |
FilterBar/types.ts | Props interfaces: FiltersBarProps, VerticalBarProps, HorizontalBarProps |
FilterBar/Vertical.tsx | Collapsible sidebar layout |
FilterBar/Horizontal.tsx | Header bar layout |
FilterBar/state.ts | Hooks: useFilters, useNativeFiltersDataMask, useFilterUpdates, useInitialization |
dataMask/reducer.ts | Redux reducer for dataMask slice |
dataMask/actions.ts | Redux actions: updateDataMask, clearDataMask, setDataMaskForFilterChangesComplete |
nativeFilters/ | Full directory: FiltersConfigModal/, FilterCard/, selectors.ts, state.ts |
dataMask — Filter State Shape#
Every filter's state is a DataMaskWithId object :
{
id: string; // filter ID (prefixed with NATIVE_FILTER_PREFIX)
extraFormData: {}; // merged into chart query formData
filterState: {}; // UI state: { value, label, validateStatus, ... }
ownState: {}; // filter-type-specific internal state
}
The global Redux slice is DataMaskStateWithId — a flat map keyed by filter ID . useNativeFiltersDataMask() narrows this map to only native filter entries by checking for NATIVE_FILTER_PREFIX.
The reducer (dataMaskReducer) handles four cases :
UPDATE_DATA_MASK— merges a single filter's new mask into Redux state.CLEAR_DATA_MASK_STATE— wipes the entire map (e.g. on dashboard unmount).HYDRATE_DASHBOARD— initializes the map fromnative_filter_configurationon load, merging any restoreddataMask(e.g. from a permalink).SET_DATA_MASK_FOR_FILTER_CHANGES_COMPLETE— reconciles after filter config edits: removes deleted filters, resets modified ones, and preserves state for filters withenableEmptyFilterordefaultToFirstItem.
Dual-State Pattern (Local Draft → Redux)#
FilterBar uses a local draft + Redux applied pattern :
- Local draft (
dataMaskSelected) — maintained viauseImmer, updated immediately on every filter interaction viahandleFilterSelectionChange. Drives the UI (e.g., Apply button enabled/disabled state). - Applied state (
dataMaskApplied) — the committed ReduxdataMask. Charts query against this state only. - Apply action —
handleApplydispatchesupdateDataMaskfor all selected filters, promoting the draft into Redux.
Filters marked with requiredFirst: true bypass the Apply step and are dispatched to Redux immediately on initialization .
Orientation: Vertical vs Horizontal#
FilterBarOrientation is an enum stored in Redux under dashboardInfo.filterBarOrientation . DashboardBuilder reads it and renders the appropriate layout.
Vertical (sidebar): rendered inside a sticky FiltersPanel with collapsible state . Requires a verticalConfig object with filtersOpen, toggleFiltersBar, width, height, and offset props . The Vertical component manages scroll detection and renders an expand/collapse toggle.
Horizontal (header): rendered inside the dashboard header area, above any tabs . Accepts only hidden and orientation props — layout config is handled internally by Horizontal.tsx . Embedded/report mode (isReport) hides the bar via the hidden prop without unmounting it, preserving filter logic .
Filter State Persistence#
When a logged-in user applies filters, FilterBar calls publishDataMask (debounced) which serializes dataMaskApplied to JSON, stores it via the keyValue API, and writes the resulting cache key to the URL as native_filters_key. On dashboard load, this key is read back and used to restore filter state . Embedded users skip this step — the user?.userId guard prevents persistence for unauthenticated contexts .
Filter Scope#
Each filter's scope object (rootPath + excluded) determines which charts it affects. See the Native Filter Scope Management article for full details on how scope is resolved and the common crash patterns around undefined scope.