Tables & Feeds
Three islands for rows you read one at a time: a table when exact values matter, a feed for things that happened in order, and a search box to find one row in many. Each is a real island below: bind it to fields in your data and the runtime renders it. See the islands overview for the full registry.
table.grid
What it is. A paginated table of raw rows. When to use it: when the exact numbers
matter and a chart would hide them: positions, transactions, line items. It needs only a
dataset; with no columns it shows every column in the data.
Positions
| Ticker | Shares | Value |
|---|---|---|
| VWCE | 312 | €38,400 |
| AAPL | 80 | €16,120 |
| MSFT | 45 | €19,850 |
| NVDA | 30 | €33,900 |
| BRK.B | 22 | €9,240 |
{
"type": "table.grid",
"title": "Positions",
"dataset": "positions",
"columns": [
{ "field": "ticker", "label": "Ticker" },
{ "field": "shares", "label": "Shares", "format": "int" },
{ "field": "value_eur", "label": "Value", "format": "eur" }
]
}Key options.
columns: [{ field, label?, format? }]: pick and order the columns and set each column's value format. Omit it to show every column.details: [{ field, label?, format? }]: fields hidden from the row and revealed in a click-to-open dialog, each with its own optional value format. Declaringdetailsmakes each row clickable.groupBy: { field, titleField?, subtitleField? }: render rows as collapsible sections byfield; the section title and subtitle read from each group's first row.drilldown: { island, match }: embed another island in the row-details dialog, its rows filtered bymatch(a map of drilldown-dataset column → clicked-row field). One level deep: a drilldown island has no drilldown of its own.expand: false: drop the see-all dialog and render every row inline (defaulttrue).
timeline.feed
What it is. A reverse-chronological feed of rows. When to use it: logs, activity
streams, a journal, anything that reads as "what happened, newest first." It needs a ts
(the timestamp) and a titleField; the runtime sorts so the newest row is on top and renders
each timestamp smartly (a date-only value as Jun 11, 2026, a real timestamp as
Jun 11, 21:30).
Activity
- Jun 13, 10:05Deposited €2,000transfer
- Jun 12, 08:30Rebalanced portfoliorule
- Jun 11, 16:48Sold 12 AAPLmanual
- Jun 10, 11:02Dividend receivedVWCE
- Jun 9, 09:14Bought 30 NVDArebalance
{
"type": "timeline.feed",
"title": "Activity",
"dataset": "activity",
"ts": "ts",
"titleField": "action",
"detail": "actor"
}Key options.
-
detail: a secondary string shown next to the title.kind: a category string. -
details/drilldown/expand: exactly as ontable.grid(click-to-open dialog, embedded filtered island, render-all toggle). -
groupBy: { field, titleField?, subtitleField? }: collapsible sections, same shape as on the table. -
Rich rows. Setting any of
highlight,stats, orfooterswitches a row from a single line to a header/stats/footer layout:highlight: { field, format?, unit? }: an emphasized value pinned to the right of the title.stats: [{ field, label?, format?, unit?, color? }]: labeled inline stats under the title; labels default to a palette,colorpins one.footer: [{ field, label?, format?, unit?, pill? }]: a meta line led by the timestamp;pill: truerenders the value as a badge.
Each of these takes an optional value format (
format) or free-formunitto style its value.
{
"type": "timeline.feed",
"title": "Workouts",
"dataset": "workouts",
"ts": "ts",
"titleField": "name",
"highlight": { "field": "strain", "format": "int" },
"stats": [
{ "field": "avg_hr", "label": "HR", "unit": "bpm" },
{ "field": "kcal", "label": "Cal", "unit": "kcal" }
],
"footer": [{ "field": "sport", "pill": true }]
}search.box
What it is. A client-side search input over a dataset. When to use it: the dataset is
large enough that you'd rather look a row up than scroll. Typing matches rows case-insensitively
across fields; results drop down as an autocomplete showing titleField, and selecting one
opens that row's full details.
{
"type": "search.box",
"dataset": "ingredients",
"fields": ["name", "category"],
"titleField": "name",
"detail": "category",
"placeholder": "Search foods…"
}Key options.
fields: [...]: the columns a query matches against (substring, case-insensitive).titleField: what each result row shows;detail: an optional secondary line under it.placeholder?: the input's placeholder text.limit?: the maximum number of visible results (default10).