Dashboard Building

Learn about widget types, controls, and JSON schema for building production dashboards with your dbt semantic layer.

Metricly dashboards are defined as JSON and can be created via chat, the visual builder, or directly in code. This guide covers the widget types, controls, and schema you need to build production dashboards.

Widget Types

Choose the right widget type for your data. Each widget type is optimized for specific use cases.

KPI

kpi

Single aggregate value with optional comparison

Use for: Totals, current values, key metrics

Total RevenueActive UsersConversion Rate

Line Chart

line_chart

Time series trends with lines

Use for: Showing change over time

Requires: grain (day/week/month)

Revenue over timeUser growth

Area Chart

area_chart

Time series with filled area

Use for: Volume and cumulative trends

Requires: grain (day/week/month)

Order volumeCumulative revenue

Bar Chart

bar_chart

Category comparison with bars

Use for: Comparing values across categories

Requires: dimension, orientation: vertical

Revenue by regionTop customers

Donut Chart

donut

Parts of a whole as percentages

Use for: Percentage breakdown

Requires: dimension

Revenue by categoryUsers by plan

Table

table

Detailed data grid

Use for: Multi-column breakdowns, detailed data

Customer list with metricsProduct performance

Heatmap

heatmap

Matrix visualization with color intensity

Use for: Cohort analysis, retention triangles

Requires: row_dimension, column_dimension

Retention by cohortActivity by day/hour

Widget JSON Schema

Every widget follows this JSON structure. Required fields are marked with *.

widget-schema.jsonjson
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"type": "line_chart", // * Widget type (see above)
"title": "Revenue Trend", // * Display title
"query": { // * Query parameters
"metrics": ["revenue"], // * Metrics to query
"dimensions": ["region"], // Optional: dimensions to group by
"grain": "$grain", // Time granularity (or use $grain)
"start_date": "2024-01-01",
"end_date": "2024-12-31",
"limit": 100,
"order_by": "revenue desc"
},
"format": { // Optional: value formatting
"type": "currency",
"currency": "USD"
},
"width": 10, // Optional: columns (1-10, default varies)
"orientation": "vertical" // Optional: for bar_chart only
}

Widget Width

Dashboards use a 10-column grid. Each widget type has sensible defaults:

KPI
width: 2
Donut
width: 3
Heatmap
width: 5
Charts/Table
width: 10

Query Parameters

The query object defines which data to fetch from your semantic layer.

ParameterTypeDescription
metrics*string[]List of metric names from your semantic layer
dimensionsstring[]Dimensions to group by (e.g., region, product_category)
grainstringTime granularity: day, week, month, quarter, year
start_datestringStart date in YYYY-MM-DD format
end_datestringEnd date in YYYY-MM-DD format
limitintegerMaximum rows to return (1-10000)
order_bystringColumn to sort by, append "desc" for descending

Dashboard Controls

Controls allow users to interact with the dashboard. Use special variables to bind widgets to controls.

Date Range

Users can select start and end dates. Bound via start_date and end_date in query params.

"query": {
  "metrics": ["revenue"],
  "start_date": "$start_date",
  "end_date": "$end_date"
}

Grain Selector

Users can switch between day/week/month/quarter/year. Bind via $grain.

"query": {
  "metrics": ["revenue"],
  "grain": "$grain"
}
When to use $grain
Use $grain when you want the widget to respond to the dashboard's grain control. Hardcode a grain value (e.g., "month") if the widget should always use a specific granularity.

Grain Options

dayDaily granularity - good for recent/short periods
weekWeekly granularity - smooths out daily noise
monthMonthly granularity - good for longer periods
quarterQuarterly granularity - business reporting
yearYearly granularity - long-term trends

Value Formatting

The format object controls how values are displayed. Match the format type to your metric.

currency

Monetary values (revenue, costs)

{ "type": "currency", "currency": "USD", "decimals": 0 }

percent

Decimal ratios displayed as % (0.32 displays as 32%)

{ "type": "percent", "decimals": 1 }

percent_value

Values already in percentage form (32.5 displays as 32.5%)

{ "type": "percent_value", "decimals": 1 }

number

Plain counts and quantities

{ "type": "number", "decimals": 0 }

compact

Large numbers abbreviated (1.2M)

{ "type": "compact" }

duration

Time values (resolution time, session length)

{ "type": "duration", "duration_unit": "minutes", "duration_style": "long" }

Complete Examples

KPI Widget

kpi-widget.jsonjson
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"type": "kpi",
"title": "Total Revenue",
"query": {
"metrics": ["revenue"],
"start_date": "$start_date",
"end_date": "$end_date"
},
"format": {
"type": "currency",
"currency": "USD",
"decimals": 0
},
"width": 2
}

Time Series Chart

line-chart-widget.jsonjson
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"type": "line_chart",
"title": "Revenue Trend",
"query": {
"metrics": ["revenue"],
"grain": "$grain",
"start_date": "$start_date",
"end_date": "$end_date"
},
"format": {
"type": "currency",
"currency": "USD"
},
"width": 10
}

Bar Chart with Dimension

bar-chart-widget.jsonjson
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"type": "bar_chart",
"title": "Revenue by Region",
"query": {
"metrics": ["revenue"],
"dimensions": ["region"],
"start_date": "$start_date",
"end_date": "$end_date",
"order_by": "revenue desc",
"limit": 10
},
"format": {
"type": "currency",
"currency": "USD"
},
"orientation": "vertical",
"width": 5
}

Retention Heatmap

heatmap-widget.jsonjson
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"type": "heatmap",
"title": "Customer Retention",
"query": {
"metrics": ["retention_rate"],
"dimensions": ["cohort_month", "tenure_month"],
"start_date": "$start_date",
"end_date": "$end_date"
},
"format": {
"type": "percent",
"decimals": 0
},
"row_dimension": "cohort_month",
"column_dimension": "tenure_month",
"row_label": "Cohort",
"column_label": "Tenure (months)",
"color_scale": "green",
"show_values": true,
"width": 10
}

Best Practices

Widget Titles

Avoid including grain words in titles since the grain can change.

Bad:Monthly Revenue
Good:Revenue

Bar Chart Sorting

Always sort bar charts by the metric in descending order for better readability. Add orientation: "vertical" for most use cases.

KPI Widgets

KPI widgets typically do not need a grain since they show totals. Only add grain if you want the KPI to show a value for a specific time period.

Use $grain for Flexibility

Bind to $grain so users can switch between day/week/month views. Only hardcode grain when a widget should never change granularity.

Heatmap Dimensions

For heatmaps, both row_dimension and column_dimension must be included in query.dimensions. Use color_scale: "green" for metrics where high is good (retention), and color_scale: "red" where high is bad (churn).

Ready to Build?