Skip to content

Blocks

Frames can be visualized by composing a tree of blocks and expressions. Roughly, expressions define what data should be queried, and blocks define how they should be visualized. Blocks can:

  • render their children in a specific layout (@block/tabs, @block/stack, @block/grid and more)
  • visualize XY charts, service maps, trace waterfalls, and more (@block/plot, @block/graph, @block/waterfall etc)
  • render content like text, markdown, and code (@block/text, @block/md, @block/code)
  • define controls such as a time range picker, filters, switches (@block/timerange, @block/field_filter, @block/switch)
  • express disclosure patterns like a drawer (@block/drawer)

Blocks sit somewhere between HTML elements (declarative) and React components (functional). Together with expressions they allow you to build a rich and interactive UI that can be statically validated and inspected, making them a perfect match for agent-authored UIs.

A block is just JSON: a single @block/<type> key with a set of properties.

{
"@block/stat": {
"title": "Error rate",
"value": { "@expr/query": "SELECT error_rate FROM requests" },
"format": "percent"
}
}

Many properties are expressible - they can either be a literal (like { "pageSize": 10}) or an expression ({ "pageSize": { "@expr/context_state": "myPageSize" } }). Expressions are reactive - which means that if your data or state changes, Noemata will automatically re-run queries and update UI where needed. Here title is a literal string and value is a query, which is what lets a static-looking block show live data.

Blocks nest. A layout block holds children (items), which can themselves be layout blocks, so a page is built by composition:

{
"@block/stack": {
"direction": "row",
"items": [
{
"@block/stat": {
"title": "Requests",
"value": { "@expr/query": "SELECT count() FROM requests" }
}
},
{
"@block/stat": {
"title": "Errors",
"value": { "@expr/query": "SELECT count() FROM errors" }
}
}
]
}
}

Getting data into a block

Blocks get their data via expressions, that can leverage both runtime state (like user preferences, or UI state) and the database’s Observability data. Expressions use the frame’s view, which has already named and shaped the data, so blocks and expressions can refer to columns and metrics by name. Queries bind to the surrounding context automatically — like the current time range, active filters, and the frame’s parameters - and queries that are bound to these values are re-executed automatically.

Block categories

  • Layout & containers — arrange and group other blocks: stacks, grids, panels, tabs, drawers.
  • Visualization — draw your data: plots, stats, tables, arcs, treemaps, graphs.
  • Inputs & controls — let the reader steer: time ranges, search, filters, switches, buttons.
  • Content & media — text and static content: Markdown, code, icons, badges, links.
  • Composition & reuse — embed frames and pull in reusable pieces and context.
  • Control flow — shape the tree itself: repeat per row, branch on a value, defer until scrolled to.

Next