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
kpiSingle aggregate value with optional comparison
Use for: Totals, current values, key metrics
Line Chart
line_chartTime series trends with lines
Use for: Showing change over time
Requires: grain (day/week/month)
Area Chart
area_chartTime series with filled area
Use for: Volume and cumulative trends
Requires: grain (day/week/month)
Bar Chart
bar_chartCategory comparison with bars
Use for: Comparing values across categories
Requires: dimension, orientation: vertical
Donut Chart
donutParts of a whole as percentages
Use for: Percentage breakdown
Requires: dimension
Table
tableDetailed data grid
Use for: Multi-column breakdowns, detailed data
Heatmap
heatmapMatrix visualization with color intensity
Use for: Cohort analysis, retention triangles
Requires: row_dimension, column_dimension
Widget JSON Schema
Every widget follows this JSON structure. Required fields are marked with *.
12345678910111213141516171819{ "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:
Query Parameters
The query object defines which data to fetch from your semantic layer.
| Parameter | Type | Description |
|---|---|---|
metrics* | string[] | List of metric names from your semantic layer |
dimensions | string[] | Dimensions to group by (e.g., region, product_category) |
grain | string | Time granularity: day, week, month, quarter, year |
start_date | string | Start date in YYYY-MM-DD format |
end_date | string | End date in YYYY-MM-DD format |
limit | integer | Maximum rows to return (1-10000) |
order_by | string | Column 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"
}$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 periodsweekWeekly granularity - smooths out daily noisemonthMonthly granularity - good for longer periodsquarterQuarterly granularity - business reportingyearYearly granularity - long-term trendsValue 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
123456789101112131415{ "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
123456789101112131415{ "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
123456789101112131415161718{ "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
123456789101112131415161718192021{ "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.
Monthly RevenueRevenueBar 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).