Filtering

Filter controls

Add a filter panel when readers need to narrow what the dashboard shows themselves, with a control bound to a param that other panels read back in their queries.

What you can do

  • A filter panel (type: filter) sets spec.control to one of 12 kinds: select, multiselect, date-range, number-range, search, toggle, number, segmented, radio, button-group, checkbox-list, or top-n.
  • valueField is required. It names the column the control reads its options or values from.
  • Single-value controls write param; the two range controls, date-range and number-range, write loParam and hiParam instead.
  • top-n adds measureField, n, and order: desc or asc; date-range supports relativeDate and presets; search takes operator and searchMode or searchParam.
  • targets is all, a list of panel ids, or an object with scope, pages, panels, mode, and bindings; each bindings[].mode can be filter, highlight, or none. dependsOn chains the control to upstream filters by param name (a parent's param, or a range parent's loParam/hiParam); see Cascading filters with dependsOn below.
  • default, values, label, placeholder, help, allLabel, unsetMode: omit or null, showOnPages, placement, width, and density shape the control; a filter panel cannot carry onClick.

Filters are panels themselves and target any data-bearing panel on the page. A filter panel cannot carry an onClick of its own.

Spec

View spec
[
  {
    "id": "region-filter",
    "type": "filter",
    "title": "Region",
    "data": {
      "query": "SELECT DISTINCT region FROM orders ORDER BY 1"
    },
    "spec": {
      "control": "select",
      "valueField": "region",
      "param": "region",
      "allLabel": "All regions",
      "targets": "all"
    }
  },
  {
    "id": "revenue-trend",
    "type": "chart:line",
    "title": "Revenue trend",
    "data": {
      "query": "SELECT order_month, SUM(amount) AS revenue FROM orders WHERE region = %(region)s GROUP BY 1 ORDER BY 1",
      "params": {
        "region": null
      }
    },
    "spec": {
      "categoryField": "order_month",
      "valueField": "revenue"
    }
  }
]

revenue-trend declares the region param with a null default and reads it via the %(region)s placeholder in its query. The value is bound, never string-interpolated.

Cascading filters with dependsOn

  • spec.dependsOn on a filter names PARAM NAMES — the parent filter's param, or a range parent's loParam/hiParam — never panel ids.
  • The child filter's own value-source query must still reference %(<param>)s with a null-guard, e.g. WHERE (region = %(region)s OR %(region)s IS NULL); dependsOn only wires the binding, it never rewrites the query.
  • dependsOn is sugar over the raw named-binding mechanism: once declared, the parent's targets entry for the child and the child's own data.params entry both become optional.
  • A dependency cycle of two or more filters — A depends on B depends on A, via dependsOn or the raw data.params binding — is rejected at save time with a 422.
View dependsOn spec
[
  {
    "id": "region-filter",
    "type": "filter",
    "title": "Region",
    "data": {
      "query": "SELECT DISTINCT region FROM orders ORDER BY 1"
    },
    "spec": {
      "control": "select",
      "valueField": "region",
      "param": "region"
    }
  },
  {
    "id": "customer-filter",
    "type": "filter",
    "title": "Customer",
    "data": {
      "query": "SELECT DISTINCT customer FROM orders WHERE (region = %(region)s OR %(region)s IS NULL) ORDER BY 1"
    },
    "spec": {
      "control": "select",
      "valueField": "customer",
      "param": "customer",
      "dependsOn": [
        "region"
      ]
    }
  }
]

Golden example: spec/examples/filter-cascading-dependson.json. The raw long-hand this desugars from — listing the child filter in the parent's targets plus an explicit data.params: {"region": null} entry on the child — still works.

Try it live

Open the interactive-filters template in the Builder

Open interactive-filters and pick a region on the segmented Region control, fv2-region-chromeless, to see the bar chart and the deals table re-query for that region.

Build it in dvt

Every interaction on this site is a few lines of the same declarative JSON spec. Read the full spec format, or connect an AI agent to your warehouse and build one live in the quickstart.

Follow the build

dvt is in founding research. Leave your email and we'll reach out when there's something to see — no newsletters, no noise.

No spam. We use this to reach out directly, nothing else.