Skip to main content

Template Syntax Reference

This page covers everything you need to know about writing templates for Dynamic Content Blocks, from basic data access to advanced techniques.

Accessing Data Fields

Basic Field Access

Reference your data fields using double curly braces with backticks for field names:

{{ rows[0].`Product Name` }}
{{ rows[0].`Revenue` }}
  • rows is an array of all data rows returned by your query
  • rows[0] accesses the first row, rows[1] the second, and so on
  • You can also use dot notation: rows.0 is equivalent to rows[0]
  • Use backticks around field names, especially those with spaces

Negative Indexing

Access rows from the end using negative indices:

{{ rows[-1].`Product Name` }}  // Last row
{{ rows[-2].`Product Name` }} // Second to last row

Raw and Formatted Values

Each field has two value types you can access:

// Get raw value (unformatted)
{{ rows[0].`Field Name`.raw }}

// Get formatted value (with formatting applied)
{{ rows[0].`Field Name`.formatted }}

When to use .raw:

  • URLs for <img src=""> or <a href="">
  • Values for inline CSS styles (e.g., percentages for width)
  • CSS class names derived from data values
  • Values without link formatting

When to use .formatted:

  • Display values with number formatting, currency symbols, etc.
  • Dates with specific format applied

Default behavior (no suffix): When you use {{ rows[0].\Field Name` }}without.rawor.formatted`, it returns a clickable drill component with the formatted value as the label. This enables cross-filtering when users click on the value.

Aggregated Values (Metrics)

Access aggregated metrics using the values accessor:

// Get all metric values for a row
{{ rows[0].values }}

// Get a specific metric value
{{ rows[0].values.`Total Revenue` }}
{{ rows[0].values.`Average Order Value` }}

Looping Through Rows

Basic Loop

Use the map function to iterate through all rows:

{% map(rows) %}
<div>{{ `Product Name` }}: {{ `Revenue` }}</div>
{% end %}

Important: Inside a map loop, reference fields directly without the rows[0] prefix.

Accessing Values Inside Loops

Inside a loop, you can access field values and metrics directly:

{% map(rows) %}
<div>
<!-- Dimension field -->
<span>{{ `Product Name` }}</span>

<!-- Metric value -->
<span>{{ values.`Revenue` }}</span>

<!-- Raw value for URLs -->
<img src="{{ `Image URL`.raw }}" />
</div>
{% end %}

Loop Example: Product List

<ul>
{% map(rows) %}
<li>
<strong>{{ `Product Name` }}</strong> - {{ values.`Revenue` }}
</li>
{% end %}
</ul>

Advanced Looping for Pivot Data

For pivot-style layouts (like heatmaps or matrices), you can loop through columns and cell values:

{% map(columns) %} - Iterate over column headers:

<thead>
<tr>
{% map(columns) %}
<th>{{ `Month Number` }}</th>
{% end %}
</tr>
</thead>

{% map(values) %} - Inside a row loop, iterate over cell values:

{% map(rows) %}
<tr>
<td>{{ `Cohort Month` }}</td>
{% map(values) %}
<td>{{ value.`Total Users` }}</td>
{% end %}
</tr>
{% end %}

Complete example - Retention Heatmap:

<table>
<thead>
<tr>
<th>Cohort</th>
{% map(columns) %}
<th>{{ `Month Number` }}</th>
{% end %}
</tr>
</thead>
<tbody>
{% map(rows) %}
<tr>
<td>{{ `Cohort Month` }}</td>
{% map(values) %}
<td style="--percentage: {{ value.`Retention Rate`.raw }};">
{{ value.`Total Users` }}
</td>
{% end %}
</tr>
{% end %}
</tbody>
</table>

Inside {% map(values) %}, use value.\Metric Name`` to access specific metrics for each cell.

Interactive Elements

Cross-filtering with Drill Components

To create clickable elements that filter other dashboard components, wrap them with the <h-drill> tag:

{% map(rows) %}
<h-drill row="0" value="{{ `Country`.raw }}">
<div class="clickable-card">
<span>{{ `Country Icon`.formatted }}</span>
<span>{{ `Country`.formatted }}</span>
</div>
</h-drill>
{% end %}
  • The <h-drill> tag wraps the clickable element
  • Use .raw for the value attribute (the actual filter value)
  • Use .formatted for display text inside the element

Styling Selected State

When a user clicks a drill element, the .h-drill-selected class is added directly to the child element inside <h-drill>. Use this class to style the active state:

<style>
.clickable-card {
border: 2px solid #E5E7EB;
padding: 1rem;
cursor: pointer;
transition: all 0.15s ease;
}

.clickable-card:hover {
border-color: #3B82F6;
background: #F8FAFC;
}

/* Style when selected - class is added to the child element */
.h-drill-selected.clickable-card {
border-color: #1B7CE4;
background: #EFF6FF;
}
</style>

Note: The selector is .h-drill-selected.clickable-card (both classes on the same element), not .h-drill-selected > .clickable-card.

Tips and Tricks

Conditional Styling with CSS Classes

Apply CSS classes dynamically based on data values:

<style>
.status-badge { padding: 0.25rem 0.5rem; border-radius: 4px; }
.status-badge.active { background: #DEF7EC; color: #047857; }
.status-badge.inactive { background: #FEE2E2; color: #DC2626; }
</style>

<span class="status-badge {{ rows[0].`Status`.raw }}">
{{ rows[0].`Status` }}
</span>

This works when your Status field contains values like "active" or "inactive" that match your CSS class names.

Working with Images

Always use .raw for image URLs:

<img src="{{ rows[0].`Image URL`.raw }}" alt="{{ rows[0].`Product Name` }}" />

Inside a loop:

{% map(rows) %}
<img src="{{ `Product Image Url`.raw }}" alt="{{ `Name`.raw }}" />
{% end %}

Build dynamic links with data values:

<!-- Email link -->
<a href="mailto:{{ rows[0].`Email`.raw }}">{{ rows[0].`Email` }}</a>

<!-- External link -->
<a href="https://example.com/product/{{ rows[0].`Product ID`.raw }}">View Details</a>

<!-- Google Maps link -->
<a href="https://www.google.com/maps/search/?api=1&query={{ rows[0].`Location`.raw }}">
{{ rows[0].`Location` }}
</a>

Using Raw Values in CSS

When you need data values in inline styles (like positioning or sizing), you must use .raw:

<div class="bar" style="left: {{ `Start Percent`.raw }}%; width: {{ `Bar Width`.raw }}%;"></div>

Best Practices

  1. Start with your data: Build and validate your dataset first using the Chart Data preview before creating your template

  2. Use the Chart Data tab: Preview your data structure to understand field names and values

  3. Test with real data: Use the live preview to see how your template renders with actual data

  4. Keep CSS scoped: Use specific class names to avoid conflicts with other dashboard elements

  5. Handle missing data: Test your template with incomplete data to ensure it fails gracefully

  6. Optimize performance: For large datasets, consider limiting rows or using filters to reduce data volume

  7. Mobile responsive: Use responsive CSS techniques if your dashboard will be viewed on mobile devices

Troubleshooting

Template not rendering

If your template isn't showing up:

  • Check for syntax errors in your HTML/Markdown
  • Ensure field names match exactly (case-sensitive) with backticks
  • Verify your data query returns results in the Chart Data tab

Data not displaying

If data fields show as empty:

  • Confirm field names use backticks: `Field Name`
  • Check if you need .raw for unformatted values
  • Verify the row index exists (e.g., rows[0] requires at least one row)

Styling conflicts

If your styles aren't applying:

  • Use specific CSS class names to avoid conflicts
  • Check browser developer tools for CSS specificity issues
  • Ensure your <style> block is included in the template

Loop not working

If your map loop isn't iterating:

  • Verify the syntax: {% map(rows) %} ... {% end %}
  • Inside loops, reference fields without rows[0]: just `Field Name`
  • Check that your data has multiple rows to iterate over

Let us know what you think about this document :)