What a visualization YAML is for
A visualization file binds one Malloy query to one chart or table. Every transformation belongs in the model query; the visualization YAML is presentation only — chart type, which fields go where, formatting, optional emphasis. For how to shape that Malloy layer (dimensions, measures, views, modular files), see Models.
For the per-type reference (every chart.* property each viz type accepts), see Visualization Types and the page for each type underneath.
Required top-level fields
id— workspace-unique identifier. Stable over time: dashboards reference it, so renaming breaks dashboards.title— human-readable title shown in the dashboard.type— viz type discriminator. Must be one of the supported types listed in the viz-types index; anything else is rejected at validation time.query— Malloy query reference, exactly in the formatpath/to/file.malloy::query_name. The path is from the workspace root; both halves must exist or validation fails.
Other top-level fields
mapping— field-role assignments. Shape varies per viz type — see the per-type page.chart— typed config block for ECharts-based types (bar, line, pie, scatter, heatmap, funnel, sankey). Closed surface: only the keys listed in the per-type reference are accepted.kpi,grid,matrix— type-specific blocks for the DOM-based types (kpi, grid, report_matrix). Use these instead ofchart.body— the Markdown body of atextnote (the one type wherequeryis optional). See text.pagination— for grid and report_matrix.filters— list of user-facing controls. See Filters.format— field-keyed number format patterns; field name → pattern string. (On thetexttypeformatis instead a scalar naming the body format —markdown.)emphasis— declarative highlight rule (per supported type — see the per-type pages).execution— execution mode override. The engine readsexecution.mode:default(implicit) ordry_run;dry_runis honored on BigQuery only and ignored with a warning elsewhere.tags— free-form labels for search.published— boolean. Onlytruevisualizations appear in dashboards.
The query reference format
Every visualization points to a Malloy query with this exact format:
query: "models/ec_revenue.malloy::by_category"
The part before :: is the path to the .malloy file from the workspace root. The part after :: is the view or query name defined inside that file. Both must match exactly.
Local validation (looky validate) checks that the file exists and the reference uses the :: separator. Server-side validation additionally compiles the model and dry-runs the query; if the view name doesn't exist, or the model fails to compile, you get a clear error before push.
Worked examples
Minimal KPI:
id: ec_revenue_kpi
title: Revenue
query: "models/ec_revenue.malloy::kpi"
type: kpi
mapping:
value: revenue
delta: revenue_delta_pct
format:
revenue: "$#,##0a"
revenue_delta_pct: "#,##0.00%"
published: true
Bar with one series — on bar, measures always go in mapping.series[]:
id: ec_revenue_by_category_bar
title: Revenue by Category
query: "models/ec_revenue.malloy::by_category"
type: bar
mapping:
x: category
series:
- field: revenue
label: Revenue
chart:
show_value_labels: true
value_label:
position: top
format:
revenue: "$#,##0.00"
published: true
Line with dual axis:
id: ec_revenue_over_time_line
title: Revenue Over Time
query: "models/ec_revenue.malloy::over_time"
type: line
mapping:
x: order_month
y: revenue
y2: order_count
series_label: Revenue
series_label_2: Orders
format:
revenue: "$#,##0"
order_count: "#,##0"
published: true
For every other shape (grouped bars, percent-stacked, donut pie, scatter, heatmap, funnel, grid with pagination, report_matrix with totals) see the per-type pages under Visualization Types.
Cross-filtering at a glance
Inside a dashboard, clicking a chart can add a "pill" that narrows every other viz on the page. Per-type emit behavior:
- Emit clicks: bar, line, pie, funnel, grid, report_matrix — plus scatter, heatmap, and sankey, which additionally require
chart.cross_filter_emitnaming what a click emits (label/x/seriesfor scatter,x/yfor heatmap,source/targetfor sankey). - Don't emit but consume pills: kpi and text (no categorical click target by structure).
- Per-viz opt-out:
chart.cross_filter: falsefor ECharts-based vizs (bar, line, pie, scatter, heatmap, funnel, sankey);grid.cross_filter: falsefor grid;matrix.cross_filter: falsefor report_matrix.
Full mechanism at Cross-filtering.
What validation actually checks (and what it does not)
Validation is two-pass — local then server-side. Per visualization, the checks are:
- Local: YAML parses;
id,title,type,queryare present and non-empty;queryuses the::separator; the model file exists; theidis unique across the workspace; thechartblock is schema-valid (every property is recognised, values are in their allowed range — errorVZ020). - Server-side: the underlying Malloy model compiles, sources are reachable, and the query dry-runs against the runtime engine. The dry run also checks that every field named in
mapping(and the other field-reference slots) exists in the query's result schema, and that every key underformatis either a mapping slot or a result field. With--strict, each query is additionally verified against the live source (cost estimate on BigQuery,EXPLAINon Postgres and MySQL).
Validation does not verify that the chart "looks right" — sizing, label collisions, color choices, and layout issues surface only at render time. Open the visualization detail page after push to catch them.
looky validate
looky list visualizations