Skip to content

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.

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 Widgets and Query Datasets work together, but they solve different problems.

Use thisFor this
Query DatasetJoins, CTEs, subqueries, window functions, and reusable SQL-defined data
Query WidgetThe final dashboard query, display settings, and Filter Variable binding
Normal widgetStandard chart, table, or KPI calculations without writing SQL
Modified DatasetNo-SQL data cleanup, filters, calculated columns, and aggregations
Joined DatasetNo-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.

  1. Open a dashboard.
  2. Click the Edit icon.
  3. Click + Widget.
  4. Choose New Query Widget.
  5. Select or paste a source in the YAML definition.
  6. Write the Widget Query.
  7. Configure the display block.
  8. Click Preview to test it.
  9. Click Save & Close.

The preview runs immediately in the editor. The saved dashboard result updates through the normal widget refresh worker.

schemaVersion: 1
source:
type: dataset
source: 46b0bbe8-619a-4b23-bc44-f11842775640
display:
type: table
timezone: America/Chicago
query: |-
select *
from source
limit 100
FieldWhat it does
schemaVersionUse 1 for the current Query Widget format
source.typedataset, modifiedDataset, joinedDataset, or queryDataset
source.sourceSource UUID copied from Browse Sources
parametersOptional Filter Variable bindings
displayControls how the result is rendered
timezoneTimezone used for date and datetime values
queryRead-only SQL that selects from the relation named source
drilldownQueryOptional SQL for on-demand detailed rows

A Widget Query can:

  • Select from source
  • Filter rows
  • Group and aggregate
  • Use case expressions
  • 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.

Most complex Query Widgets should point at a Query Dataset:

schemaVersion: 1
source:
type: queryDataset
source: 5a242d39-195c-41da-a99b-93b402a4325b
display:
type: table
timezone: America/Chicago
query: |-
select *
from source
order by last_email_age_minutes desc
limit 100

Even though the source is a Query Dataset, the Widget Query still selects from source. Do not select from the Query Dataset UUID.

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-d196b91752c1

Then reference the parameter in SQL:

select count(*) as ticket_count
from source
where board_name in $board_values

Use 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.

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_count
from source
where dashboard_date_filter(created_at)
group by created_day
order by created_day

If no dashboard date filter is active, the helper allows all rows.

display:
type: number
value: ticket_count
rounding: 0
query: |-
select count(*) as ticket_count
from source
display:
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: true
query: |-
select
ticket_number,
ticket_subject,
board_name,
last_email_age_minutes
from source
order by last_email_age_minutes desc
limit 100
display:
type: bar
x: board_name
y:
- ticket_count
xLabel: Board
yLabel: Tickets
dataLabels: true
query: |-
select
board_name,
count(*) as ticket_count
from source
group by board_name
order by ticket_count desc
display:
type: line
x: created_day
y:
- ticket_count
areaFill: true
query: |-
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_day
display:
type: pie
label: status_name
value: ticket_count
donut: true
query: |-
select
status_name,
count(*) as ticket_count
from source
group by status_name

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_count
from source
where "board.name" = 'Service Board'

Better yet, alias it to a simple name in a Query Dataset:

select tickets."board.name" as board_name
from tickets

Then the Query Widget can use board_name.

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 desc

The 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.

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.

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.

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:

  1. Confirm the widget was saved.
  2. Check that the source dataset or Query Dataset has synced.
  3. Wait for the dashboard refresh worker to process the queued update.
  4. 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 source

Do not use the source UUID or a Query Dataset alias in a Query Widget.

Move the join into a Query Dataset, save it, then use that Query Dataset as the Query Widget Source.

Quote dotted column names:

"board.name"

Make sure the SQL result uses the exact column name referenced by display.

display:
type: number
value: ticket_count
select count(*) as ticket_count
from source