Site block types
When the core blocks are not enough, an editor or site administrator can create a block type for the site. People then place it on pages like any other block and fill in its fields; they never see its structure, markup or CSS.
Recommended workflow: ask a connected AI agent to design the type, expose editable fields, validate it and prepare a preview. Use the editor to review and fine-tune the result; everyday content changes then use those fields. Create your own block type includes a sample request and the tool sequence. The reference below is useful to the agent and to people implementing types directly.
There are two kinds:
| Composed (recommended) | Template | |
|---|---|---|
| Built from | Existing blocks: heading, text, image, icon, button, container, repeater … | Your own HTML |
| Fields reach it by | Bindings such as {{props.title}} on the inner blocks |
Tokens such as {{title}} in the markup |
| Styling | Named styles and settings of the inner blocks, plus optional scoped CSS | Scoped CSS |
| Use when | Almost always: you keep named styles, image handling, links and accessibility | The markup cannot be built from existing blocks |
Both are available in the portal (Blocks), the API and MCP, with the same checks. Creating, changing, deleting and importing block types needs write permission on the site from Core 0.2.78. Editors can author types as well as fill in their fields. A changed block type reaches the live site with the next deploy.
For a first example, follow Create your own block type. The built-in catalog helps you check whether an existing type already meets the need.
Fields
Section titled “Fields”schema lists the fields people edit. Every field has name, type and
label, and may have help, required and default.
| Type | Holds |
|---|---|
text, textarea |
Plain text, always escaped. |
richtext |
Formatted text (sanitized HTML). |
image |
An image from the media library or a URL. |
icon |
An icon name; {name}_svg is the icon itself. |
link |
{ page_id } for a page on the site, or { url }, plus new_tab. It resolves to href, target and rel, and follows the page when its address changes. Only http, https, mailto and tel addresses are used. |
select |
One of options (with optional option_labels). |
boolean, number, color |
As named. |
array |
A repeating group with its own fields. item_label names the field that titles each item in the editor; min_items and max_items limit the count. Groups nest three levels deep. |
object |
A fixed group of fields. |
Field names are lowercase letters, digits and _. Names ending in _svg or
_html are reserved for derived values.
Composed block types
Section titled “Composed block types”A composed block type has a composition: a tree of blocks. Inner blocks read
the block’s fields (its props) through bindings, an inner field whose whole
value is {{props.name}}. Bindings keep their type: a list stays a list, a
link stays a link, true stays true.
To repeat something for every item of a list field, put a repeater in the
composition, bind its items to the list ("{{props.items}}") and leave its
item block empty. Its children are then the template for one item, and read the
item with {{item.title}}, {{item.link.href}} and so on.
A container set to link (tag: "a") with href: "{{item.link.href}}" makes
the whole item a link. When an item has no link, the container renders as a plain
container instead, so linked and unlinked items share one design (render
version 4).
The icon list, with an icon, a heading and a line of text in each item, all wrapped in a link:
{ "name": "icon_list", "label": "Icon list", "category": "content", "schema": [ { "name": "items", "type": "array", "label": "Items", "item_label": "title", "min_items": 1, "fields": [ { "name": "icon", "type": "icon", "label": "Icon", "default": "check" }, { "name": "title", "type": "text", "label": "Heading" }, { "name": "text", "type": "text", "label": "Text" }, { "name": "link", "type": "link", "label": "Link" } ] } ], "composition": [ { "id": "list", "type": "core/repeater", "data": { "source_type": "static", "items": "{{props.items}}", "layout": "list" }, "children": [ { "id": "item", "type": "core/container", "data": { "tag": "a", "href": "{{item.link.href}}", "new_tab": "{{item.link.new_tab}}", "direction": "row", "gap": "md", "padding_y": "none", "css_class": "icon-list-item" }, "children": [ { "id": "icon", "type": "core/icon", "data": { "icon": "{{item.icon}}", "size": "lg" } }, { "id": "title", "type": "core/heading", "data": { "text": "{{item.title}}", "level": "h3" } }, { "id": "text", "type": "core/text", "data": { "text": "{{item.text}}" } } ] } ] } ], "styles": ".icon-list-item { color: inherit; text-decoration: none; }"}core/text holds one line or paragraph of plain text in a chosen element. Use
it rather than a text block when a value comes from a field, so the value is
never treated as HTML.
Composed block types can use other composed block types, four levels deep, never in a loop. In the editor a placed composed block is one unit: you select it and edit its fields.
Template block types
Section titled “Template block types”A template block type has its own template (HTML). Tokens:
| Token | Output |
|---|---|
{{title}} |
The field value, escaped. |
{{{body}}} |
Raw HTML, only for richtext fields and derived HTML (icon_svg). |
{{link.href}} |
A sub-value of a link or object field. |
{{#title}}…{{/title}} |
Shown when the field has a value. |
{{^title}}…{{/title}} |
Shown when the field is empty. |
{{#each items}}…{{/each}} |
Repeated for every item of a list field; names resolve against the item first. {{@index}}, {{@number}}, {{@first}} and {{@last}} give the position. |
{{#link link class="row"}}…{{/link}} |
Wrapped in <a> when the link resolves, rendered without a link otherwise. |
The same icon list as a template:
<ul class="icon-list"> {{#each items}} <li> {{#link link class="icon-list__link"}} <span class="icon-list__icon">{{{icon_svg}}}</span> <strong>{{title}}</strong>{{#text}} <span>{{text}}</span>{{/text}} {{/link}} </li> {{/each}}</ul>Output passes through the HTML sanitizer, so a template can use links, images and SVG but never script.
Name the elements of the template a site may want to restyle in parts, such
as { "name": "title", "label": "Title", "selector": "li strong", "target": "heading" }.
Named styles then reach them on every block (a style’s parts:
icon_list.title) or on one block (Appearance → Part styles,
data.part_styles), without CSS against the block’s markup. See
Parts on site block types for every
property and the selector rules.
A block type’s styles apply only to its own blocks. Typeroll prefixes every
selector with the block, so .icon-list-item matches only inside an icon list.
:scope is the block element itself (:scope { display: grid }). Selectors for
html, body or :root are refused. @media and @container work as usual.
Use the site’s design values (var(--color-primary)) and
named styles instead of fixed colours, so the block follows the
site’s look.
Preview, validation and starters
Section titled “Preview, validation and starters”Every write is checked before it is stored. Problems come back with a path
(/schema/0/fields/2/name) or a template line, and errors refuse the write.
| Action | MCP tool | API below /api/v1/sites/{siteId} |
|---|---|---|
| List and read | list_block_types, read_block_type |
GET /block-types, GET /block-types/{id} |
| Starters (icon list, feature cards, steps) | list_block_type_starters |
GET /block-types/starters |
| Check a definition without saving | validate_block_type |
POST /block-types/validate |
| Preview an unsaved definition | preview_block_type |
POST /block-types/preview |
| Create, update, delete | create_block_type, update_block_type, delete_block_type |
POST /block-types, PATCH/DELETE /block-types/{id} |
| Where it is used | find_pages_using_block_type |
GET /block-types/{id}/usage |
| Export and import | export_block_types, import_block_types |
.tcblocks files |
The preview uses the same rendering pipeline, Site render version, theme, named styles and other block types as publication. It does not execute block scripts; test scripted behavior in a suitable deployment before launch.
Linked items, such as those in the icon list and feature card starters, need render version 4 or later. Before it, an item without an address is still a link, and every icon renders inside a link of its own, which nests links in a linked item. A write that puts an icon in a linked container on such a site returns a warning; upgrade the site’s rendering first.
Changing a block type in use
Section titled “Changing a block type in use”Renaming a field moves the data on every page, template, header, footer, global
block and draft that uses the block type: send
renames: { "old_name": "new_name" } (or "items.old": "new" inside a group)
with the update. Removing a field or changing its type when blocks hold values
in it is refused with a list of where it is used, unless you send
confirm_data_loss: true. A block type that is in use cannot be deleted.
Sharing between sites
Section titled “Sharing between sites”Export block types to a .tcblocks file and import it on any site, in any
Organization or Typeroll installation. On import, a block type whose name
already exists is skipped, renamed or replaced, as you choose.