Board Content¶
Boards can contain narrative content using the content field. This is useful for adding context, explanations, or summary text alongside your data.
Basic Content¶
Add content at the top level of a board:
title: "Executive Summary" text: | ## Q4 Performance Revenue is up **15%** quarter-over-quarter. rows: - revenue_chart
The board's title: already renders as an <h1>, so start your body text at ## (h2) or as a paragraph; don't repeat the title with a leading # Title, or it will render twice. To change which heading level titles render at (for example, when embedding a board inside a page that already owns H1/H2), see Title Heading Levels below.
Nested Content¶
Use content in nested boards to create rich, document-like layouts:
rows: - text: | ## Monthly Analysis Here is the breakdown of our performance by region. - cols: - text: | ### Key Takeaways - North region is leading - South region needs attention - text: | ### Action Items 1. Increase ad spend in South 2. Optimize North inventory
Mixed Content Layouts¶
Combine content blocks with charts for rich, document-like dashboards:
source: examples_db
title: "Sales Report"
charts:
revenue_chart:
query:
sql: |
SELECT category, SUM(revenue) AS revenue, SUM(units_sold) AS units_sold FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
type: bar
title: "Revenue by Category"
x: category
y: revenue
color: category
rows:
- text: |
This report shows **Q4 performance** across product categories.
- cols:
- revenue_chart
- text: |
### Key Takeaways
- **Electronics**: Top performer
- **Accessories**: Growing steadily
- **Tools**: Stable revenue
Markdown Features¶
Because content is Markdown, you can use all standard Markdown features:
Tables¶
text: | | Metric | Value | Notes | | --- | --- | --- | | Revenue | $4.2M | `SUM(revenue)` over the last quarter | | Margin | 32% | Consistent with forecast |
Images¶
Drop in logos, diagrams, or badges:
text: | # dbt-powered Insights  Our revenue model is compiled with dbt.
A project image (.png, .jpg, .jpeg, .gif, .svg, .webp, .bmp) is embedded directly in the render, so it shows up the same way in SVG/PNG/PDF export, dct serve, and Cloud, with no separate asset hosting to set up. Its path takes one of two forms:
- A relative path resolves against the directory of the file it is written in: the board's own file, or the file an imported board or chart comes from.
- A path starting with
/resolves from the project'scharts/directory, the directorydct serveserves.
text: |  
The image fills its column at its own aspect ratio. http(s):// and data: image URLs work too and are never fetched over the network at render time. A project image's path is written out, not built by a template: {% if %} can choose between whole images, and alt text can be templated, but a {{ }} path that renders to a project file renders as an error. A templated http(s):// URL is fine. A path that doesn't exist, or that isn't one of the supported image types, renders as an error naming the path and the directory it was looked for in; a relative path that only exists under charts/ says to write it with a leading /.
An image also renders inline, wherever it sits: mid-sentence, in a heading, a list item, or a table cell. A table of thumbnails is one markdown table:
text: | | Logo | Company | | --- | --- | |  | Acme Corp | |  | Globex Inc |
Callouts & Quotes¶
text: | > :sparkles: Markdown lets you mix prose, tables, inline `code`, > and callouts in a single content block.
Links¶
text: | Want a deeper dive? [Open the dbt docs](https://docs.getdbt.com/) or [view another board](../guides/getting-started.md).
Inline Code¶
text: | Reference variables like `{{ selected_month }}` or metric names like `total_revenue`.
Title Heading Levels¶
style.title.level controls the H-level (H1–H6) used for board and chart titles.
# Default: compute from the count of titled ancestors. # A titled root board is H1, a titled child section is H2, and so on. # Bare layout wrappers (no title) do not advance the counter. style: title: level: auto
# Lock the level to a specific heading, useful for embedded boards where # H1/H2 are already owned by the outer page. This board and all descendants # start from H3; titled children render at level + 1, and bare wrappers # inherit the locked level unchanged. style: title: level: 3
Query values in text and titles¶
text:, board title:, and a chart's title, subtitle, KPI label and callout title and message read query results through queries.<name>. The query is defined under queries: like any other; text runs after it executes.
queries: summary: columns: [net_revenue, vs_plan, churn] values: - [4210000.5, 0.042, null] top_regions: columns: [region, revenue] values: - [EMEA, 1800000] - [AMER, 1500000] - [APAC, 910000] text: | {% set s = queries.summary.one %} Net revenue is **{{ s.net_revenue | format('currency') }}**, {{ 'ahead of' if s.vs_plan >= 0 else 'behind' }} plan by {{ s.vs_plan | abs | format('percent') }}. Churn: {{ 'not available' if s.churn is none else s.churn | format('percent') }}. Top regions: {% for r in queries.top_regions.rows %} - **{{ r.region }}**: {{ r.revenue | format('currency') }} {% endfor %} Leading: {{ queries.top_regions.rows | map(attribute='region') | join(', ') }}. {{ queries.top_regions.rows | length }} regions; {{ queries.top_regions.rows[0].region }} leads.
Renders as:
Net revenue is **$4.21mn**, ahead of plan by 4.2%.
Churn: not available.
Top regions:
- **EMEA**: $1.8mn
- **AMER**: $1.5mn
- **APAC**: $910k
Leading: EMEA, AMER, APAC.
3 regions; EMEA leads.
| Form | Meaning |
|---|---|
queries.q.one.col |
Scalar. Requires exactly 1 row. |
queries.q.rows |
List of rows. for, length, [i], map and join work. [i] past the end is an error, and so is first, last, min, max or random on an empty list. |
queries.q.columns |
Column names. [i] past the end is an error. |
row.col, row['col'] |
Raw cell value. A column named items or values is that column. |
v \| format(spec) |
Formats v with a preset, a board format alias, or a d3 or strftime string. Large numbers use narrative notation ($4.21mn, 910k); an alias that sets notation: keeps its own. |
{{ queries.q.one.col }} |
Python str() of the raw value (4210000.5). Nothing is formatted implicitly. |
.oneraises unless the query returned exactly one row. UseLIMIT 1or.rows[0]to pick a row;.rows[0]on a query with no rows is an error too. There is no.first.- A NULL cell is
none. Guard withx is nonebefore comparing;x > 0.05on NULL is an error. - In text and titles,
formatreplaces Jinja's printf-styleformatfilter. It takes exactly one spec;{{ '%.1f' | format(x) }}is an error. - A column whose name starts with
_is read with a subscript,row['_fivetran_synced'];row._fivetran_syncedis an error. - Reference queries by literal name:
queries.nameorqueries['name'].queries[var],queries | listand{% set q = queries %}are errors. - Unknown queries and non-literal use fail at compile (
dct validate). Row-count, column and NULL errors fail at render: a board title ortext:error is the board's error, and a chart's text error shows in that chart's card while the rest of the board renders. - An imported chart's text reads the queries of the file the chart is defined in.
- A board whose title reads query data or uses
formatis as wide as prose, one readable line (style.text.column.max_charscharacters, 75 by default), not as wide as the title.
Titles and chart text¶
title: "Q3 revenue {{ queries.summary.one.net_revenue | format('currency') }}" queries: summary: columns: [net_revenue, vs_plan] values: - [4210000.5, 0.042] by_region: columns: [region, revenue] values: - [EMEA, 1800000] - [AMER, 1500000] charts: regions: query: by_region type: bar x: region y: revenue title: "{{ queries.by_region.rows | length }} regions" subtitle: "{{ queries.summary.one.vs_plan | format('percent_delta') }} vs plan" headline: query: summary type: kpi value: net_revenue label: "Net revenue ({{ 'ahead' if queries.summary.one.vs_plan >= 0 else 'behind' }})" rows: - headline - regions
The board title reads "Q3 revenue $4.21mn", the bar chart's title "2 regions" with subtitle "+4.2% vs plan", and the KPI label "Net revenue (ahead)".
Raw HTML Content¶
Set html_policy: trusted-raw at the board root to render text: as raw HTML.
See HTML Policy for the full tier table, security notes, and
guidance on when to use each tier.
Content Tips¶
- Use tables to align KPIs and explainers side by side
- Add
> blockquotesas callouts for assumptions or warnings - Drop
[links](...)to help viewers jump to related docs - Embed inline
`code`to reference variables or query snippets - Combine headings, lists, and images for mini write-ups
Related¶
- Boards Overview - Basic board structure
- Layouts - Organizing content with layouts
- HTML Policy - Raw HTML tiers and security notes
- Examples - Complex layout patterns