Expressions & calculations
Insert live data into any block with {{$…}} expressions, compute with JSONata, and reshape bindings with transforms.
#Expressions: {{$…}}
Any text-bearing block config (headings, text, badges, stats, tiles, buttons, card link patterns, calendar month…) can embed live values:
| Expression | Resolves to |
|---|---|
{{$binding.key.field}} |
A field from a binding. On a list binding this reads the first row — handy for "top item" callouts. |
{{$binding.key}} |
The whole binding value — useful for computed bindings (see transforms). |
{{$record.field}} |
The current record — on record pages, and row-scoped inside repeaters. |
{{$user.name}} / {{$user.email}} / {{$user.role}} |
The viewing user. |
{{$site.name}} |
The instance name. |
{{$url.param}} |
A URL query parameter. |
{{$time}} |
The render time (ISO). |
Missing values render as empty — a typo never breaks the page. Plain-text output is always escaped; HTML modes sanitise after insertion, so data can't inject markup.
#Calculations: {{= … }}
For math, string operations, and aggregates, use a JSONata calculation:
{{= $count(binding.movies.data) }} films
{{= "$" & $formatNumber($sum(binding.movies.data.revenue) / 1e9, "#,##0.0") & "B" }}
{{= $substring(record.release_date, 0, 4) }}
Calculations see binding, record, user, site, url, and now. They evaluate
against the page's top-level data — use {{$record.field}} for per-row values inside
repeaters.
#Binding transforms
A binding's Transform field runs JSONata over the fetched value before blocks see it. Three patterns cover most needs:
data[status="published"] → filter rows
data[vote_average >= 8]^(>vote_average) → filter + sort (descending)
data ~> |$|{"decade": $string($floor($number($substring(release_date,0,4))/10)*10) & "s"}|
→ derive a field on every row
{"revenue": $sum(data.revenue), "count": $count(data)}
→ compute an object; read it as
{{$binding.key.revenue}}
Derived fields behave exactly like stored ones — chip rules, kanban lanes, and table columns can all use them. A failing transform keeps the untransformed data, so a bad expression can't blank a page.
Tip: a second binding on the same collection with a different transform costs nothing extra — collection queries are batched per render.