Query Widgets
Query Widgets let you write SQL for a dashboard widget. They are useful when a normal widget calculation is too limited, or when you want to build a widget on top of a reusable Query Dataset.
A Query Widget has one source. That source can be:
- A Dataset
- A Modified Dataset
- A Joined Dataset
- A Query Dataset
Inside the Widget Query, the selected source is always named source.
When to use a Query Widget
Section titled “When to use a Query Widget”Use a Query Widget when you need:
- A table, number, bar chart, line chart, pie chart, gauge, or funnel from SQL
- Custom grouping or filtering that is easier to express in SQL
- A dashboard widget over a Query Dataset
- Filter Variables passed into SQL as safe parameters
- A small final query on top of a reusable source
Use a normal widget when the built-in calculation editor already covers the metric. Normal widgets are still easier for common counts, sums, averages, grouped charts, and simple table views.
Query Widget vs Query Dataset
Section titled “Query Widget vs Query Dataset”Query Widgets and Query Datasets work together, but they solve different problems.
| Use this | For this |
|---|---|
| Query Dataset | Joins, CTEs, subqueries, window functions, and reusable SQL-defined data |
| Query Widget | The final dashboard query, display settings, and Filter Variable binding |
| Normal widget | Standard chart, table, or KPI calculations without writing SQL |
| Modified Dataset | No-SQL data cleanup, filters, calculated columns, and aggregations |
| Joined Dataset | No-SQL joins and unions across datasets |
If your SQL needs a join, put that join in a Query Dataset first. Then create a Query Widget that selects from the Query Dataset.
Creating a Query Widget
Section titled “Creating a Query Widget”- Open a dashboard.
- Click the Edit icon.
- Click + Widget.
- Choose New Query Widget.
- Select or paste a source in the YAML definition.
- Write the Widget Query.
- Configure the
displayblock. - Click Preview to test it.
- Click Save & Close.
The preview runs immediately in the editor. The saved dashboard result updates through the normal widget refresh worker.
Basic YAML structure
Section titled “Basic YAML structure”schemaVersion: 1source: type: dataset source: 46b0bbe8-619a-4b23-bc44-f11842775640display: type: tabletimezone: America/Chicagoquery: |- select * from source limit 100Fields
Section titled “Fields”| Field | What it does |
|---|---|
schemaVersion | Use 1 for the current Query Widget format |
source.type | dataset, modifiedDataset, joinedDataset, or queryDataset |
source.source | Source UUID copied from Browse Sources |
parameters | Optional Filter Variable bindings |
display | Controls how the result is rendered |
timezone | Timezone used for date and datetime values |
query | Read-only SQL that selects from the relation named source |
drilldownQuery | Optional SQL for on-demand detailed rows |
Widget Query rules
Section titled “Widget Query rules”A Widget Query can:
- Select from
source - Filter rows
- Group and aggregate
- Use
caseexpressions - Sort and limit rows
- Use declared Query Parameters such as
$board_values - Use
dashboard_date_filter(column)for the active dashboard date filter
A Widget Query cannot:
- Join another source
- Use CTEs
- Use subqueries
- Use unions
- Select from a source name or UUID directly
- Change data
- Read or write files
These rules keep saved widgets predictable. If you need joins or CTEs, create a Query Dataset first.
Querying a Query Dataset
Section titled “Querying a Query Dataset”Most complex Query Widgets should point at a Query Dataset:
schemaVersion: 1source: type: queryDataset source: 5a242d39-195c-41da-a99b-93b402a4325bdisplay: type: tabletimezone: America/Chicagoquery: |- select * from source order by last_email_age_minutes desc limit 100Even though the source is a Query Dataset, the Widget Query still selects from
source. Do not select from the Query Dataset UUID.
Using Filter Variables
Section titled “Using Filter Variables”Query Widgets do not automatically inherit every dashboard filter. Add Query Parameters for the Filter Variables you want to use.
parameters: - name: board_values filterVariable: 32b69796-14b3-48d1-a9a7-d196b91752c1Then reference the parameter in SQL:
select count(*) as ticket_countfrom sourcewhere board_name in $board_valuesUse in for multi-select Filter Variables. Use = for single-select Filter
Variables.
Parameters are sent to the query safely. They are not pasted into the SQL as text.
Using the dashboard date filter
Section titled “Using the dashboard date filter”To use the active dashboard date filter, call dashboard_date_filter() with the
date or datetime column you want to filter:
select date_trunc('day', created_at) as created_day, count(*) as ticket_countfrom sourcewhere dashboard_date_filter(created_at)group by created_dayorder by created_dayIf no dashboard date filter is active, the helper allows all rows.
Display examples
Section titled “Display examples”Number
Section titled “Number”display: type: number value: ticket_count rounding: 0query: |- select count(*) as ticket_count from sourcedisplay: type: table columns: - ticket_number - ticket_subject - board_name - last_email_age_minutes columnLabels: ticket_number: Ticket ticket_subject: Subject board_name: Board last_email_age_minutes: Age in minutes columnFormats: last_email_age_minutes: rounding: 0 fitToWidth: truequery: |- select ticket_number, ticket_subject, board_name, last_email_age_minutes from source order by last_email_age_minutes desc limit 100Bar chart
Section titled “Bar chart”display: type: bar x: board_name y: - ticket_count xLabel: Board yLabel: Tickets dataLabels: truequery: |- select board_name, count(*) as ticket_count from source group by board_name order by ticket_count descLine chart
Section titled “Line chart”display: type: line x: created_day y: - ticket_count areaFill: truequery: |- select date_trunc('day', created_at) as created_day, count(*) as ticket_count from source where dashboard_date_filter(created_at) group by created_day order by created_dayPie chart
Section titled “Pie chart”display: type: pie label: status_name value: ticket_count donut: truequery: |- select status_name, count(*) as ticket_count from source group by status_nameQuoting columns with dots
Section titled “Quoting columns with dots”Some source columns include dots, such as board.name or company.identifier.
In SQL, a dot usually means a table or struct field. If the column name itself
contains a dot, wrap it in double quotes:
select count(*) as ticket_countfrom sourcewhere "board.name" = 'Service Board'Better yet, alias it to a simple name in a Query Dataset:
select tickets."board.name" as board_namefrom ticketsThen the Query Widget can use board_name.
Drilldown queries
Section titled “Drilldown queries”Use drilldownQuery when you want a Query Widget to provide detailed rows for a
modal or export.
drilldownQuery: |- select ticket_number, ticket_subject, board_name, last_email_age_minutes from source where board_name in $board_values order by last_email_age_minutes descThe drilldown query uses the same source and Query Parameters as the main Widget Query. Keep the filters aligned so the detailed rows match the visible chart or number.
Refresh behavior
Section titled “Refresh behavior”Preview runs immediately. Saved widgets update through the dashboard refresh pipeline.
For a normal Dataset, Modified Dataset, or Joined Dataset source, the widget refreshes when that source updates and the dashboard is eligible to refresh.
For a Query Dataset source, the widget watches all base datasets used by the Query Dataset. The refresh interval is based on the most frequently updated source.
Example:
- Tickets sync every 1 minute.
- Ticket audit trail syncs every 15 minutes.
- A Query Dataset joins both.
- A Query Widget uses that Query Dataset.
The Query Widget uses a 1-minute target interval, and it can update when either related source changes.
Dashboards that have not been viewed recently may refresh less often. Opening the dashboard again makes it eligible for normal updates.
Working with AI assistants
Section titled “Working with AI assistants”The Query Widget editor is friendly to AI-assisted drafting because the whole definition is YAML. You can use Copy AI Instructions, then paste the current definition, relevant columns, and the preview error into a chat.
Useful prompt example:
I am editing a Resplendent Query Widget Definition.
Source:- type: queryDataset- source: 5a242d39-195c-41da-a99b-93b402a4325b
Columns:- board_name: String- ticket_number: Int64- ticket_subject: String- last_email_age_minutes: Float64
Goal:Create a table showing the 100 oldest tickets by last outgoing email age.Use the Query Widget boundary, so no joins or CTEs.If the assistant writes a join, ask it to move that logic into a Query Dataset.
Troubleshooting
Section titled “Troubleshooting”Preview works, but the saved widget keeps loading
Section titled “Preview works, but the saved widget keeps loading”Preview runs in the editor. Saved widgets render in the background. If a saved widget keeps loading:
- Confirm the widget was saved.
- Check that the source dataset or Query Dataset has synced.
- Wait for the dashboard refresh worker to process the queued update.
- Reopen the editor and preview again to check for definition errors.
Widget Query must select from exactly one source relation named source
Section titled “Widget Query must select from exactly one source relation named source”The SQL must use:
from sourceDo not use the source UUID or a Query Dataset alias in a Query Widget.
Joins are not allowed
Section titled “Joins are not allowed”Move the join into a Query Dataset, save it, then use that Query Dataset as the Query Widget Source.
Referenced table "board" not found
Section titled “Referenced table "board" not found”Quote dotted column names:
"board.name"The display says a column is missing
Section titled “The display says a column is missing”Make sure the SQL result uses the exact column name referenced by display.
display: type: number value: ticket_countselect count(*) as ticket_countfrom source