Author your first dashboard
Learn the anatomy of a DAC dashboard - rows, widgets, filters, queries, templating - in both YAML and TSX. Write one against any table in your own database.

What you'll do
Learn the building blocks of a DAC dashboard - filters, rows, widgets, queries, templating - so you can write one against your own tables without leaning on the starter.
Why this matters
Every DAC dashboard is the same recipe: a connection, some filters, a grid of widgets, and the SQL behind each one. Once you can read one, you can review one in a PR. Once you can write one, every dashboard in your org becomes a diff instead of a meeting.
The vocabulary
Five primitives make up every dashboard:
- Dashboard - the top-level definition:
name,connection, optional sharedqueries, and a list ofrows. - Filter - a user input (
select,date-range,text) that rewrites widget SQL via Jinja templating. - Row - a horizontal band that holds one or more widgets.
- Widget - a single visual element. Each widget declares its
type, itscolwidth, and the SQL it pulls from. - Query - the SQL that produces a widget's data. Inline under a widget, named under a top-level
queries:block, or referenced from a.sqlfile inqueries/.
The grid is 12 columns wide. The col values inside a row must add up to 12.
Widget types
type | Renders | Required fields |
|---|---|---|
metric | A single number with optional prefix/suffix | sql, column, format |
chart | line, bar, area, pie, scatter, ... | sql, chart, x, y (array) |
table | A scrollable result grid with column labels | sql (or query), columns |
text | Markdown content | content |
image | A static image | src, alt |
divider | Visual separator | (none) |
chart: accepts the full ECharts catalogue - line, bar, area, pie, scatter, bubble, funnel, sankey, heatmap, boxplot, waterfall, and more.
Filter types
type | What the user sees | Useful defaults |
|---|---|---|
select | Drop-down of values (static list or query-driven) | "All" |
date-range | Date picker with presets | "last_30_days", "all_time" |
text | Free-form input | "" |
Reference filters in your SQL with Jinja: {{ filters.region }} and {% if filters.region != 'All' %}...{% endif %}. A date-range filter exposes .start and .end (e.g. {{ filters.date_range.start }}).
Anatomy of a dashboard file
DAC reads two formats from dashboards/: *.yml / *.yaml and *.tsx. Same logical structure, different syntax. Pick the one that fits how your team works - YAML for declarative, TSX for programmatic (loops, conditionals, shared components).
name: <Dashboard Name>
description: <one-line summary>
connection: <connection name from .bruin.yml>
filters:
- name: <filter name>
type: select
default: All
options:
values: [All, <option_a>, <option_b>]
queries: # optional - shared queries by name
<query_name>:
sql: |
SELECT ... FROM <your_table>
{% if filters.<filter name> != 'All' %}
WHERE <column> = '{{ filters.<filter name> }}'
{% endif %}
rows:
- widgets:
- name: <Metric Title>
type: metric
col: 4 # 12-column grid
column: value # which column of the result is the number
prefix: "$"
format: number
sql: |
SELECT SUM(<column>) AS value FROM <your_table>
- name: <Chart Title>
type: chart
chart: line
col: 8
sql: |
SELECT <x_col>, SUM(<y_col>) AS <y_alias>
FROM <your_table>
GROUP BY 1 ORDER BY 1
x: <x_col>
y: [<y_alias>]
Tip
TSX files accept plain JSX-style code too - if you don't want types, just leave them off. The file extension still has to be .tsx for DAC to discover it; .jsx files are not recognised.
Templating cheatsheet
The same Jinja runtime works in YAML and inside TSX sql={\...`}` strings.
{{ filters.X }}- inserts the value of filter X.{{ filters.date_range.start }}/{{ filters.date_range.end }}- fields on a date-range.{% if filters.X != 'All' %}...{% endif %}- skip aWHEREwhen the user picked the default.{% if filters.X == '<value>' %}...{% else %}...{% endif %}- branch on a value.
Validate before you serve
Three commands, in order:
dac ls --dir .
dac validate --dir .
dac check --dir .
dac ls- shows what DAC discovered. Use this when a new file isn't being picked up.dac validate- checks structure and schema only (no DB hit). Fast.dac check- runs every query against the live connection. Run this beforedac serveto catch a bad column or table name without opening a browser.
What just happened
You now have the vocabulary and the schema to write a DAC dashboard against any table in any connection - and the validation pipeline to catch mistakes before they reach the browser.