Skip to content

Variables

Variables describe the filters and inputs that live inside the dashboard shell. Each definition renders a labeled control, and the runtime keeps queries and charts in sync when values change. Variables must be explicitly referenced in query filters so behavior stays predictable.

Variable names must be unique within a file and across all imported files.


How Variables are Defined

Every entry under variables needs a name and a source (column, query, MetricFlow, or dbt). The input widget is optional — when omitted, the compiler auto-detects it from the other fields (e.g. optionsselect, min/maxslider, boolean defaultcheckbox). The compiler:

  • derives a title (label) and placeholder from the slugified name unless you override them,
  • validates that the chosen input matches the source type,
  • keeps default undefined unless you explicitly set it.

Variables do not auto-apply themselves. Wire them in your query filters (plain names, Jinja expressions, helper functions) so you stay in control.

variables:
  region:
    column: orders.region
    input: select

  product_family:
    query: queries.product_family_list
    input: multiselect

  location:
    dimension: geography.region
    input: select

  profit_metric:
    measure: financial.profit
    input: select

  territory:
    model: analytics.orders
    column: territory
    input: select

Inputs and Type Mapping

The input field is optional. When omitted, the compiler auto-detects the widget from the variable's other fields (options → select, min/max/stepslider, boolean default → checkbox, list default → multiselect, hidden with no other signals → text). You can still set input explicitly to override auto-detection. The table below captures the available types; daterange is the default for date or timestamp sources so you can work with start/end values in expressions.

For a complete reference of all input types and their options, see UI Elements.

Input Description Default Type
select Single-choice dropdown string
multiselect Dropdown with multiple choices array / string[]
input / text Free-form text string
textarea Multi-line text string
slider Numeric slider (min, max, step required) number
datepicker Single date picker date (for rare single-date filters)
daterange Date range picker date / timestamp (preferred default)
checkbox Boolean toggle boolean
radio Small set of explicit options string

Column Binding (most common)

column binds a variable to a table column (<tablename>.<columnname>). The compiler reads the column’s metadata (type, enums, nullability) to populate options, determine operators, and suggest placeholder text. Column bindings still require explicit wiring inside query filters.

variables:
  region:
    column: orders.region
    input: select

Add operator or default only if you need to override the default behavior. Variables are optional by default; add required: true only when the dashboard cannot render without a value.

required: true is a generic render-time contract, not a chart-specific feature. Any query or chart that depends on that variable will fail to render until the value is supplied, typically through a URL query parameter or a variable default: in the face YAML.

Query-Powered Variables

query draws variable options from custom SQL. Pass either an existing query name (query: queries.regions) or inline SQL. The result must include at least one column for the value; additional label or description columns improve the UI.

variables:
  brand:
    query: |
      SELECT brand_id AS value, brand_name AS label
      FROM brands
      ORDER BY brand_name
    input: select

MetricFlow Integration

Use dimension or measure to reference MetricFlow entities. Dataface fetches type, values, descriptions, and roll-ups from the semantic layer so you rarely duplicate metadata.

variables:
  geography:
    dimension: geography.region
    input: select

  revenue_metric:
    measure: revenue.total
    input: select

dbt Integration

model + column refers to a dbt model column. The manifest resolves the model and metadata, giving you the same experience as a regular column.

variables:
  territory:
    model: analytics.orders
    column: territory
    input: select

Additional Options

  • default: initial value before user interaction
  • placeholder: override the auto-generated hint
  • required: block rendering until a value is supplied
  • options: static list or dynamic_query reference for select/multiselect
  • min/max/step: required for sliders
  • operator: controls how the value becomes a filter expression

When a required variable is missing, Dataface returns the normal structured render error. The fix is to either open the board with the needed query parameter, or define default: for that variable.

Wiring Variables to Queries

Variables must be referenced explicitly in query filters (plain name, Jinja expression, or helper function). The compiler turns those references into SQL filters, and the runtime refetches whenever a referenced variable changes.

When a variable is unset or null, filters should gracefully handle this by returning 1=1 (always true) so no filtering is applied. This is the default behavior when using the filter() helper macro.

queries:
  sales:
    sql: |
      SELECT * FROM orders
      WHERE {{ filter('region', '=', region) }}
        AND {{ filter_date_range('order_date', date_range) }}
    source: my_postgres

For simple filter references (non-SQL queries), you can use direct variable references:

queries:
  sales:
    filters:
      region: region
      date: "{{ date_range.start }} to {{ date_range.end }}"

For formatting, conditional logic, and helper macros, see Expressions.

Reactive Behavior

  • Queries refresh when their referenced variables change.
  • Charts redraw when their queries finish.
  • Debouncing, optimistic loading, and errors are handled automatically.

Example: Column-bound Filter

By default, variables are unset (null) until a user selects a value. Use the filter() macro in SQL queries to handle this gracefully—when the variable is null, it returns 1=1 (no filter applied).

variables:
  region:
    column: orders.region
    input: select
    # No default - variable starts as null/unset

queries:
  sales:
    sql: |
      SELECT
        region,
        SUM(revenue) as total_revenue
      FROM orders
      WHERE {{ filter('region', '=', region) }}
      GROUP BY region
    source: my_postgres

When region is null/unset, the filter returns 1=1 and all regions are shown. When a user selects a region, the filter becomes region = 'North' (or whatever was selected).

For more examples and details on the filter() macro, see Expressions.

Interactive Variables

Every rendered dashboard includes the current variable values as ordinary SVG text. Browsers progressively enhance those values into interactive controls. If JavaScript does not run or the SVG consumer does not support foreignObject, the read-only values remain visible instead.

SVG, HTML, PNG, and PDF therefore share the same rendered dashboard. PNG and PDF conversion uses the read-only presentation, while browsers reveal the controls and allow users to change the values.

How It Works

  1. Each variable renders its current value as SVG text
  2. In a browser, each variable with an input type becomes an HTML form control
  3. When the user changes a value, the dashboard re-renders with the new variable value
  4. Variable values can be used in chart titles, queries, and conditional logic via Jinja templates

Supported Interactive Input Types

Input HTML Control Description
select <select> dropdown Single-choice selection
text / input <input type="text"> Free-form text entry
number <input type="number"> Numeric input with min/max/step
slider / range <input type="range"> Slider with value display
checkbox <input type="checkbox"> Boolean toggle
date / datepicker <input type="date"> Date selection

Example: Interactive Dashboard

title: Sales Dashboard

variables:
  region:
    label: Region
    input: select
    options:
      static: [North, South, East, West]
    # No default - starts unset, shows all regions

  min_revenue:
    label: Min Revenue
    input: number
    min: 0
    max: 10000
    step: 100
    # No default - starts unset, no minimum filter

queries:
  sales:
    sql: |
      SELECT product, SUM(revenue) as revenue
      FROM orders
      WHERE {{ filter('region', '=', region) }}
        AND {{ filter('revenue', '>=', min_revenue) }}
      GROUP BY product
    source: my_postgres

charts:
  sales_chart:
    title: "Sales{% if region %} for {{ region }} Region{% endif %}"
    type: bar
    query: sales
    x: product
    y: revenue

rows:
  - sales_chart

Static Options

For interactive-only variables (not bound to a data source), use options.static. Note: variables don't need an "All" option—when unset, they simply don't apply any filter.

variables:
  category:
    input: select
    options:
      static: [Electronics, Accessories, Tools]
    # No default - starts unset, shows all categories

queries:
  products:
    sql: |
      SELECT * FROM products
      WHERE {{ filter('category', '=', category) }}
    source: my_postgres

These variables don't need a column, query, or other source—the options are defined statically in the YAML. When the variable is unset (no selection), the filter() macro returns 1=1 and all categories are shown.

Using Variables in Templates

Variables can be used anywhere Jinja templates are supported:

  • Chart titles: title: "Sales for {{ region }}"
  • Query SQL: WHERE region = '{{ region }}'
  • Conditional rendering: visible: show_details

Built-in Directory Variables

Four variables are automatically injected into the Jinja render context for every dashboard, scoped to the face file's own directory. They require no variables: declaration and are always available in text: blocks and titles.

These are author-injected built-ins, distinct from the declared variables: block covered above. They have no UI control and are computed at render time from the face's location on disk.

Name Type Description
this_dir object The directory containing the face file
parent_dir object or None The parent directory; None at the project root
siblings list Entries in the face's own directory (one level)
tree string Pre-rendered markdown indented listing of the directory

Object shapes

this_dir and parent_dir are plain objects with three fields:

Field Value
name Directory name (e.g. reports)
path Path relative to the project root (e.g. faces/reports)
url Serve URL with trailing slash (e.g. /faces/reports/)

siblings is a list; each entry has:

Field Value
name File or directory name (e.g. summary.yml)
url Serve URL (extension stripped for files; trailing slash for directories)
is_dir true for directories, false for files
ext File extension (.yml, .yaml, .md) or empty string for directories
label Filename stem — use this for display (e.g. summary for summary.yml)

tree is a pre-rendered markdown string of the directory tree, depth-bounded to avoid pathological trees. Drop it directly into a text block or a code fence.

Example: landing page that lists its siblings

A dashboard at faces/reports/index.yml can list its neighbors:

title: Reports
text: |
  ## {{ this_dir.name }}

  {% for s in siblings %}
  - [{{ s.label }}]({{ s.url }})
  {% endfor %}

Or show the full tree:

title: Reports
text: |
  {{ tree }}

Or link to the parent directory:

text: |
  {% if parent_dir %}
  ← [Back to {{ parent_dir.name }}]({{ parent_dir.url }})
  {% endif %}

Scope and limits

  • Variables are scoped to the face's own directory — there is no way to point at a different directory in v1 (deferred).
  • The label field uses the filename stem only. Reading each sibling's frontmatter title: is not done at this stage because compiling every sibling is expensive.
  • The variable name set is closed: this_dir, parent_dir, siblings, tree. Do not declare variables: entries with these names — user-declared variables would shadow the built-ins (user wins, per Jinja merge order).

Advanced Topics

For nested dependencies, conditional filters, and layout orchestration driven by variables, see Advanced Variables.

  • UI Elements – Complete reference for all input types and options
  • Expressions – Formatting, conditional logic, and the filter() macro
  • Advanced Variables – Dependent variables and conditional layouts
  • Queries – How variables plug into query filters
  • Charts – Interactions that set variables or add filters