# Page block types

_Every block a page or dashboard can contain, with its stored settings, nesting rules, data dependencies, and limitations._

Canonical: https://snoze.dev/help/page-blocks/

A Page or Dashboard stores one row per block. Every row has a kind, an order
key, a JSON manifest for that kind, and an optional parent. Text is rich inline
content: text and hard breaks can carry bold, italic, underline,
strikethrough, code, link, colour, and comment marks. Inline content can also
contain user, item, record, date, and field mentions, maths, and footnotes.

The canonical block vocabulary below is generated from the contract.

<!-- GENERATED:page-block-kinds:START -->

Canonical contract values:

- `paragraph`
- `heading_1`
- `heading_2`
- `heading_3`
- `heading_4`
- `heading_5`
- `heading_6`
- `bulleted_list_item`
- `numbered_list_item`
- `to_do`
- `toggle`
- `quote`
- `callout`
- `code`
- `divider`
- `image`
- `bookmark`
- `embed`
- `file`
- `video`
- `audio`
- `column_list`
- `column`
- `database_view`
- `page_link`
- `table_of_contents`
- `equation`
- `table`
- `synced_page`
- `footnote`
- `metric`
- `insight`
- `activity_feed`
- `button`
- `form_embed`
- `drive_embed`
- `control`
- `template_text`
- `dashboard_tabs`
- `repeat`
- `doc`
- `agenda`
- `clock_in`
- `timesheet`
- `tabs`
- `tab`
- `mermaid`
- `breadcrumb`
- `record_embed`
- `connector_view`

<!-- GENERATED:page-block-kinds:END -->

## Text blocks

### Paragraph `paragraph`

The default rich-text block. It stores rich content, a palette colour,
horizontal and vertical alignment, and dashboard card appearance. A Paragraph
may contain child blocks, which is how indented content is represented.

### Heading 1 `heading_1`

The largest rich-text heading. It has the same colour, alignment, vertical
alignment, and card appearance settings as a Paragraph. Headings contribute to
a Table of contents. Setting `toggleable` makes it a toggle heading: its child
blocks fold under it, and only a toggle heading takes an indented block. A
plain heading stays a flat section title.

### Heading 2 `heading_2`

A second-level rich-text heading. Its settings, `toggleable` option and Table
of contents behaviour match Heading 1; only its semantic and visual level
differ.

### Heading 3 `heading_3`

A third-level rich-text heading with the same colour, alignment, appearance,
and outline behaviour as the other headings.

### Heading 4 `heading_4`

A fourth-level rich-text heading. Use it below a Heading 3 in a document
outline; it has no kind-specific settings beyond the shared heading settings.

### Heading 5 `heading_5`

A fifth-level rich-text heading. It is stored as its own kind so the document
outline and renderer retain the intended hierarchy.

### Heading 6 `heading_6`

The smallest rich-text heading. It completes the six stored outline levels and
has the same shared heading settings.

### Bulleted list item `bulleted_list_item`

One unordered-list row. It stores rich content and colour and may contain
children for nested content. Alignment is deliberately not stored because the
list gutter stays left-anchored.

### Numbered list item `numbered_list_item`

One ordered-list row. Consecutive items are rendered as a numbered sequence.
It stores rich content and colour and may contain children; it has no alignment
setting.

### To-do `to_do`

A rich-text task with a `checked` boolean and colour. Ticking it writes the
block manifest; it is not a database Checkbox and does not become filterable in
a database view. It may contain child blocks.

### Toggle `toggle`

A rich-text disclosure label whose child blocks are the collapsible content.
It stores rich content and colour. The open or closed editor state is not part
of its persisted manifest.

### Quote `quote`

An aligned rich-text quotation. It supports colour, horizontal and vertical
alignment, dashboard appearance, and child blocks.

### Callout `callout`

A highlighted aside with rich content, an optional icon (an emoji), colour,
and a `variant` tone: `info`, `success`, `warning`, `danger`, or `neutral`.
Without an icon the tone's default emoji shows. It can contain child blocks.
It does not store the broader dashboard card appearance settings used by
Paragraph and Quote.

### Code `code`

A code block stores plain `code`, a language identifier, an optional caption,
and whether long lines wrap. It is not rich text and Snoze does not execute it;
use a Code Action when code must run.

### Divider `divider`

A horizontal or vertical separator. It stores `orientation` plus dashboard
background, border, radius, shadow, and padding appearance. It contains no
content.

## Media and link blocks

### Image `image`

An image can reference an uploaded `fileId` or a URL. It stores a rich caption,
numeric width, alignment, and a full-width switch. A URL is presentation data;
the manifest does not promise that Snoze has copied the remote asset.

### Bookmark `bookmark`

A rich link preview. It stores the URL plus cached title, description, image,
site name, icon, and a rich caption. Those preview fields are a snapshot and
may become stale when the destination changes.

### Embed `embed`

An external URL shown inline. It stores height, alignment, full-width state,
and a rich caption. An optional `provider` (`youtube`, `vimeo`, `loom`,
`figma`, `google_maps`, `spotify`, `codepen`, `miro`, `google_drive`) shapes
the empty block's prompt and accepts only that provider's links. Whether the
destination renders depends on that site's embedding policy; a valid URL does
not guarantee it permits framing.

### File `file`

An uploaded or linked file tile. It stores `fileId` or URL, display name, byte
size, and a rich caption. This is page content, separate from a File field on a
database record.

### Video `video`

An uploaded or linked video with a rich caption, width, alignment, and
full-width state. The manifest does not define transcoding or a guaranteed set
of remote video hosts.

### Audio `audio`

An uploaded or linked audio clip with a rich caption. It stores `fileId` or
URL, but has no width or alignment settings.

## Layout and structure blocks

### Column list `column_list`

The structural parent for side-by-side Columns. It has no manifest settings
and may contain only `column` blocks. A Column list cannot be placed inside a
Column or another Column list.

### Column `column`

One child of a Column list. `widthRatio` controls its share of the row. It can
contain ordinary blocks, but not a Column or Column list, and it cannot exist at
the document root.

### Tabs `tabs`

A tabbed container. It has no settings and may contain only `tab` blocks; one
panel shows at a time, and which one is open is each viewer's own view state.
Tabs cannot be nested inside tabs. Exports and phones show every panel in
order.

### Tab `tab`

One panel of a Tabs block, with a `label` of up to 80 characters. It holds
ordinary blocks and cannot exist outside a Tabs block.

### Breadcrumb `breadcrumb`

The page's place in the workspace: its parent folders and pages, resolved live
from the item tree, so moving the page updates the trail. It has no settings.

### Mermaid diagram `mermaid`

Mermaid source in `code` (up to 20,000 characters) with an optional rich
caption, shown as a rendered diagram beside its code while editing. Rendering
uses Mermaid's strict security level: no click handlers, scripts or raw HTML.

### Table of contents `table_of_contents`

A computed outline of the page's heading blocks. It has no stored settings or
hand-written entries; the client derives the outline from Heading 1–6 blocks.

### Equation `equation`

A display-maths block. It stores a formula string and optional rich caption.
Its schema accepts the string; whether a particular expression renders is up
to the maths renderer.

### Table `table`

A static grid with row and column counts, a two-dimensional array of rich-text
cells, and optional column widths. It has no typed fields, filters, sorts, or
records. Use a Database view for data that needs those features.

### Footnote `footnote`

The stored body for a footnote, identified by `footnoteId` and containing rich
text. Inline footnote nodes refer to it. It is not a page footer or citation
manager.

## Workspace and data blocks

### Database view `database_view`

An inline view of a database. New embeds store `databaseId`, `viewKind`, and an
owned `viewManifest`, so changing the embed does not mutate a saved database
view; `viewId` is a legacy saved-view pointer. Parameter filters can bind
dashboard parameters into the view, `selectionParam` can publish the current
selection, and `restrictAccess` hides the embed from people without target
access. The block also supports dashboard card appearance.

### Page link `page_link`

A card that opens another workspace item by `itemId`. It can enforce target
access with `restrictAccess` and supports dashboard card appearance. It is a
reference, not a copy of the target page.

### Synced page `synced_page`

A live rendering of another block surface selected by `surfaceId`, with an
option to show its title. Edits affect the referenced surface rather than a
detached copy. The current contract does not store a frozen version or a
fallback snapshot if that surface becomes unavailable.

### Metric `metric`

A data widget: one or more metrics drawn as a number (KPI), line, area,
column, bar, combo, donut, funnel, waterfall, table, leaderboard, progress,
gauge, or heatmap. A metric is either an aggregate — `count`, `count_unique`,
`sum`, `avg`, `median`, `min`, `max`, or `latest` over a database or a Snoze
app (time entries, shifts, calendar events, activity), with an
optional filter and date field — or a formula over other metrics, such as
`[Revenue] - [Expenses]`, which may combine different databases.

On a Dashboard, metrics usually live in the dashboard's metric library so
every widget reads the same definition; a widget can also carry its own. The
widget groups by a date grain, a field, or a shared field, can split series
by a second grouping, and follows the dashboard's period and comparison
unless it sets its own. It also sets sort, a top-N limit with the rest
folded into Other, whether empty groups show, and visual options (stacking,
labels, legend, sparkline, target, thresholds). Metric presentation covers
number, currency, percent, and duration formats, decimals, compact notation,
prefix and suffix, whether higher is better, and a target.

Metrics saved before the metric library (a single `databaseId`, `fn`,
`fieldKey`, and `display`) still render, and are upgraded to the current
shape the next time they are edited.

### Insight `insight`

A durable written briefing. It stores title, body, the prompt that produced it,
refresh time, and source item IDs. Refreshing writes ordinary block data, so
everyone sees the same reviewable result; it is not recomputed separately for
each viewer. It supports access restriction and card appearance.

### Activity feed `activity_feed`

A recent-workspace-events feed with an optional item limit and dashboard card
appearance. The client caps the catch-up feed; this is not an unbounded event
archive or a configurable event query.

### Form embed `form_embed`

An inline workspace Form selected by item ID. It uses the form's own questions,
validation, target database, and submission behaviour. `restrictAccess`
controls whether a viewer without target access sees it, and card appearance
controls its dashboard frame.

### Drive embed `drive_embed`

The workspace drive and folder browser, optionally starting at `folderId`.
With no folder it starts at the workspace root. It owns its navigation state
and supports access restriction and dashboard card appearance.

## Interactive dashboard blocks

### Button `button`

A labelled control bound either to a saved Action by `actionId` or to a local
command: open an item, set a dashboard parameter, or open a dashboard tab.
Legacy inline refresh/create/open bindings still decode, but editors migrate
them when possible. Server actions retain their own permissions and run
history; a button is not an authority bypass.

### Control `control`

An input that writes a named dashboard parameter. Its control can be `select`,
`search`, `date`, `member`, `segmented`, or `toggle`, with an optional label and
placeholder. Other blocks must explicitly bind to the same parameter name;
the control does not filter every widget automatically.

### Template text `template_text`

Presentation text with a template string and an explicit token list. Tokens
can read dashboard parameters and data exposed by the runtime. They render as
ordinary text, not HTML and not an unrestricted expression language. The
block also supports alignment and card appearance.

### Dashboard tabs `dashboard_tabs`

A child-bearing tab container with one to eight tab definitions and dashboard
card appearance. Its children are assigned to tabs by the dashboard runtime.
Tab containers cannot be nested inside each other.

### Doc `doc`

A small document inside a dashboard card: headings, paragraphs, lists,
to-dos, toggles, quotes, callouts, code, dividers, tables, and equations,
edited with the same editor as Pages — including the `/` menu and Markdown
paste. Its blocks are stored in the widget itself (up to 300), with an
optional title and card appearance. People who can edit the dashboard can
edit a Doc in place without entering edit mode.

### Agenda `agenda`

Upcoming calendar events the viewer can see, for today, this week, the next
7 days, or the next 30 days, optionally limited to chosen calendars and a
number of events. It reads as the viewer and shows nothing on published pages.

### Clock in `clock_in`

Clock in and out with the running timer for the viewer, optionally with who
on the team is on shift now. It needs time tracking enabled in the workspace.

### Timesheet `timesheet`

Tracked hours per day for this week, last week, this month, or the last 7
days — for the viewer or the whole team, with rows per member or per tracked
item. It needs time tracking enabled in the workspace.

### Repeat `repeat`

Repeats its child block subtree for each value in `fieldKey`, with an optional
label. Relation values scope a copy to each related record; other multi-value
fields expose the current value through a synthetic `value` key. A Repeat can
contain children but cannot contain another Repeat. It needs a collection in
the surrounding dashboard data context; a field key alone does not fetch a
database.

### Record embed `record_embed`

A live card for one database record, stored as `databaseId` and `recordId`.
It shows the record's title, status, assignee and due date as they change and
opens the record on click. Record access rules still apply to every viewer.

### App data `connector_view`

A live table from a connected app — Linear or Jira issues, Stripe invoices, a
Notion database — shown as a table, list, cards or one number. Type `/App
data`, pick the app and table, fill in what to read, then choose columns. It
stores only the setup (`action`, `input`, `connectionId`, `display`,
`columns`, `limit`, `title`, `aggregate`); nothing is copied into Snoze. Each
viewer reads the app with their own access, and **Save to database** copies
the rows shown into a new database when you want them to stay. Private apps
(email) show only to their owner; everyone else sees "Private to its owner".
Published pages and share links never read an app and show a placeholder.

## Nesting and appearance

Paragraph, headings, list items, To-do, Toggle, Quote, Callout, Repeat, Column
list, Column, Tabs, Tab, and Dashboard tabs may contain children; a heading
holds children as a toggle heading. The structural restrictions
described above are validated when blocks are inserted or moved.

Dashboard-capable blocks may store a background, card background and border
colours, border width from 0–4, radius from 0–40, shadow (`none`, `soft`,
`medium`, or `bold`), and padding (`compact`, `comfortable`, or `spacious`).
Not every block kind opts into those appearance fields.