Array template
Field-level catalog tables below stay in English — they match the machine design schema used by the editor, API, and MCP.
List/repeater primitive: repeats its content slot once per item of an
arrayorentry[]variable (or array-like binding). SetarrayBindingto a whole-value token such as{{var:hits}},{{var:items}},{{bind:requests.<name>.results}}, or{{bind:…}}. Inside the slot the current row is aliased asitem— use{{var:item.<fieldKey>}}(and nested paths). A text array item is the whole{{var:item}}. Forentry[]results, object field keys are flattened fromfieldValuesontoitemso tokens omit.fieldValues. OptionalcolumnsByBreakpoint(+ gaps) lays rows out as a CSS grid (each row is a grid item).minItems(default 1) hides the public template when there are fewer items — use 2 for a size selector that should disappear on a one-size product. Prefer columns on the template rather than wrapping it in a single GridListBlock cell. A GridListBlock cell is one direct child, so a card is one wrapper. For outer gaps or columns around the template, use FlexListBlock or GridListBlock. Row DOM ids are suffixed with-{index}. Child blocks can use shellhideWhenEmpty: "{{var:item.link}}"to skip empty fields, orvariableVisibilitywithvalue: "item.<field>". Prefercreate_component_presetkind=list. Do not invent a list mega-block. Stored key path:layout.columnsByBreakpoint(+ the two gap maps), not a top-levelcolumnsByBreakpoint. Inside a reusable component, set it on this block in the component document (get_components then save_component); a ComponentBlock mount on the page has no layout props, andshell.layout.*on the mount is saved but ignored.
Examples
Search hits list
{
"arrayBinding": "{{var:hits}}",
"content": [
{
"type": "HeadingBlock",
"props": {
"text": "{{var:item.title}}",
"fontSize": 24
}
}
]
}Component input items
{
"arrayBinding": "{{var:items}}",
"content": [
{
"type": "HeadingBlock",
"props": {
"text": "{{var:item.name}}"
}
}
]
}Fields
content
Template slot rendered once per array item. Tokens resolve in a scope where the current row is item ({{var:item.<fieldKey>}}, or {{var:item}} for a scalar). Empty arrays render nothing on the published site; the editor shows a single sample row.
content: [{ type: "HeadingBlock", props: { text: "{{var:item.title}}" } }]arrayBinding
Whole-value {{var:id}} or {{bind:id}} selecting an array or entry[] source. Nested object arrays use {{var:parent.field}}. Partial/string interpolation is ignored — binding must be the entire token.
arrayBinding: "{{var:hits}}"emptyContent
Empty Content.
emptyContent: []minItems
Minimum item count before the public template renders. Default 1. Set 2 so a single option (a one-size product) renders no selector. The editor still shows the sample row.
minItems: 2layout
columnsByBreakpoint
Column count. A number applies to every breakpoint, or a map keyed by layout breakpoint id (from get_theme section=layout).
layout: {
columnsByBreakpoint: 3
}columnGapByBreakpoint
Gap between columns, in pixels. A number applies to every breakpoint, or a map keyed by layout breakpoint id.
layout: {
columnGapByBreakpoint: 16
}rowGapByBreakpoint
Gap between rows, in pixels. A number applies to every breakpoint, or a map keyed by layout breakpoint id.
layout: {
rowGapByBreakpoint: 16
}<details><summary>Default props</summary>
{
"arrayBinding": "",
"content": [],
"emptyContent": []
}</details>