A template is a custom visual that runs no author JavaScript: you write visual., visual. and options., and DataPrism fills the markup with the tile's rows. This page is the reference for the template language. For the editor around it, see The custom visual Studio; for what a tile receives, Custom visual options, data and limits; and for custom visuals as a whole, Custom visuals and the visual library.
{{ . . . }} draws a value into the markup as text. Markup a value carries stays text: it is never parsed as HTML.{{{ . . . }}} draws raw markup. Its output passes the sandbox's sanitiser first, so scripts, frames and event attributes never survive it.true or false.+ joins text and numbers.row, as in row. Total.{{ Total | number:2 }}. An argument follows the filter name and may be quoted with single or double quotes.number groups digits and sets the decimals you ask for: with number:2 a value draws with grouped digits and two decimals, such as 1,234. 57. The argument must be a whole number from 0 to 20; anything else fails with <filter>:<digits> expects an integer between 0 and 20, got '<value>'. An empty value stays empty, and text that is not a number passes through unchanged.percent reads a fraction as a percentage: 0. 312 draws 31. 2%; an empty value stays empty.abbrev shortens large numbers to K, M, B and T; the output looks like 1. 2K, 3. 4M, 5. 6B or 7. 1T, and a value below 1,000 stays as it is.date draws a date through a pattern (see below). It works in wall-clock terms, is never shifted to the viewer's timezone, and reads a numeric value as epoch milliseconds.text draws a value as text; upper and lower change case and are safe for scripts other than Latin.default replaces an empty value with a fallback and keeps 0.format draws a value with a field's own format rules; it must sit on a field, as in row. Total | format, or name one: | format:'<FieldId>'. Without a field it fails with the format filter needs a field: write row. <Field> | format, or | format:'<FieldId>'.unknown filter '<name>'.yyyy-MM-dd.yyyy and yy for the year; MMMM, MMM, MM and M for the month; dd and d for the day; HH, H, hh and h for the hour; mm and m for the minute; ss and s for the second; EEEE and EEE for the weekday; a for the AM or PM marker. Text in single quotes draws as written.17. 09. 2026, 4 Mar 2026, March, Wednesday or at 3:05 PM.||; &&; == and !=; <, <=, > and >=; + and -; *, / and %. ! and - negate a value, and ? with : chooses between two values. % is the remainder.true, false and null.unknown function '<name>'; allowed functions: <list>.access to '__proto__' is not allowed.expression nesting deeper than <limit>.unterminated string starting at column <n> and unexpected character '<character>' at column <n>. An empty expression fails with empty expression.Every directive is an attribute on the element it affects.
data-uni-repeat repeats the element once per item: the tile's rows (rows), a field's groups (groups('Field')), or a group's rows (g. rows). The expression must produce a list; otherwise it fails with data-uni-repeat="<expression>" did not produce a list; use rows, groups('Field') or g. rows.data-uni-if keeps the element while its expression is true. It works on plain elements and inside repetitions alike.data-uni-select="<Field>" selects that field's value when the element is clicked; Ctrl or Cmd adds to the selection. The field must be in scope through a row or a group; otherwise it fails with data-uni-select="<field>" needs a row or a group of that field in scope; put it inside data-uni-repeat. The host keeps the element's data-uni-selected state set to on or off, and data-uni-suppress="select" makes a click do nothing.data-uni-class adds classes to the element, and data-uni-style-* sets style properties. A custom property uses a doubled dash: data-uni-style---uni-gap sets --uni-gap. token('chart-1') resolves a theme token, such as hsl(var(--chart-1)).url(), expression() or javascript:. An element whose style value does is dropped, with style value for '<name>' may not contain url(), expression() or javascript:.@import; the rule is removed, with @import is not allowed in template CSS; the rule was removed.data-uni-src sets an image from an https address or asset:<name>. The image's src appears only after the app has delivered it; a bad binding reads data-uni-src needs an https URL or "asset:<name>", got '<value>', and an asset that is not attached reads unknown asset '<name>'.data-uni-embed embeds a player from a fixed https address; anything else reads data-uni-embed needs an https URL, got '<value>'.index is the item's position, and the item's fields are in scope by name.groups('Field') repeats once per group of that field. A group carries g. key (its value), g. count (its rows), g. rows (the rows themselves) and g. sum(. . . ) (a total across them).count() and sum(. . . ); count() totals the rows, as the starter's {{ count() }} rows bound shows.<div data-uni-repeat="rows">
<span data-uni-select="Category">{{ Category }}</span>
<strong>{{ Amount | number:2 }}</strong>
</div>{{ Category }} draws the row's category and {{ Amount | number:2 }} draws the amount with grouped digits and two decimals.unknown field '<name>'; available fields: <fields>, and inside a repetition the item's context is added, such as (repeat item 0).more than 20000 nodes and keeps what it built so far.Use the number filter with the decimals you want: {{ Total | number:2 }} draws with grouped digits and two decimals, such as 1,234.. The argument must be a whole number from 0 to 20. An empty value draws nothing, and text that is not a number passes through unchanged.
A value that arrives as a digit string of 16 or more characters is grouped as a string rather than turned into a number, so nothing is rounded and no digit is lost: a value such as 92,233,720,368,547,758,070 draws in full.
Month and weekday names always read in English, because the template locale is fixed, whatever the viewer's language.
No. Dates draw from their wall-clock fields, as written, and are never converted to the viewer's timezone. A date that already carries a zone in its text draws from its wall-clock fields too.
Put data-uni-repeat="rows" on the element; it draws once per row the tile receives. Inside it, the row's fields are in scope by name and index gives the item's position. To repeat per group of a field instead, use groups('Field').
Add data-uni-if with the condition. The element draws while the expression is true and is left out otherwise, on plain elements and inside repetitions alike.
Repeat over groups('Field'). Each group carries g., g., g. and g. for totals across it, and the scope carries count() and sum(. as well.
Only the built-in ones. A call to anything else fails to compile, and the error lists the functions you may use.
Put data-uni-select="<Field>" on it and keep that field in scope through a row or a group, usually by working inside a repetition. A click then selects the value; Ctrl or Cmd adds to the selection, and data-uni-suppress="select" stops a click from selecting.
No. {{ . draws every value as text, so markup a value carries stays visible as text. Only {{{ . draws markup, and its output passes the sandbox's sanitiser first.
Yes, 20,000 nodes per render, elements and text together. Past that the render stops with one error and keeps what it built so far, so a very large listing draws partially and reports more than 20000 nodes.
A style value may not contain url(), expression() or javascript:, so the element is dropped and the error reads style value for '<name>' may not contain url(), expression() or javascript:. Load outside images through data-uni-src instead.