Row-scoped Bindings
Repeatables and Tables render the same blocks once per row, so a binding written once has to mean something different in every row. Row-scoped bindings are how that works: inside a row, a handful of extra names resolve to this row's data, and references to sibling blocks resolve to this row's copy of them.
Inside a Table: currentRow
A Table column's value expression sees currentRow — the record behind the row being rendered. It is a reserved name; you cannot name an entity currentRow, so it always means the row.
| Name | Resolves to | Example |
| currentRow | The full data record for this row. | {{ currentRow.status }} |
| currentRow.field | Any field from this row's data object. | {{ currentRow.qty * currentRow.price }} |
Table cell expression examples
// A field from this row {{ currentRow.status }} // Computed per row {{ currentRow.qty * currentRow.price }} // Conditional per row {{ currentRow.status === 'overdue' ? 'red' : 'green' }} // Optional chaining for nullable fields {{ currentRow.customer?.name ?? 'Unknown' }}
Warning: Inside a Table's column value expressions, the utils namespace is not the standard helper set. A cell expression that formats with utils.formatDate may produce an empty result even though the identical expression works elsewhere on the page. Format dates and numbers in the data source, a page function, or use the column's built-in formatting options.
Blocks Inside a Table Row
Blocks placed inside a Table (rather than in a column value expression) reach the row through the Table's name:
{{ my_table.context.currentRow.status }}
Inside a Repeatable: Row Context
Each row of a Repeatable carries three values, reached through the Repeatable's own name:
| Binding | Resolves to |
| {{ my_repeatable.context.item }} | This row's complete data item. |
| {{ my_repeatable.context.index }} | This row's position, starting at 0. |
| {{ my_repeatable.context.primaryKey }} | This row's identity key. |
A block inside row 4 that binds {{ my_repeatable.context.item.title }} shows row 4's title; the same binding in row 5 shows row 5's. You write it once, on the template.
Repeatable row context examples
// This row's title {{ my_repeatable.context.item.title }} // This row's position (0-based) {{ my_repeatable.context.index + 1 }} // A computed value for this row {{ my_repeatable.context.item.quantity * my_repeatable.context.item.price }}
Note: While editing an expression on a block inside a Repeatable, the builder previews values using the first data item. Whatever you see in the builder is always row one's values — even if you clicked on a different row.
Sibling Block Resolution
Inside a row, a plain reference to a sibling block by name resolves to that sibling's copy in the same row, not to a page-wide value. An action inside a row that submits a form submits this row's form, not every row's form.
With Repeatables nested inside Repeatables, the innermost row wins: a name resolves against the closest row that actually contains that block, walking outward only if the inner row doesn't.
Reaching Across Rows: instances
From anywhere — inside a row or outside the Repeatable entirely — the Repeatable exposes instances, an array with one entry per row.
| Key on each instance | Description |
| currentIndex | The row's position (0-based). |
| currentPrimaryKey | The row's identity key. |
| (block names) | One entry per direct child block of the Repeatable, holding that row's copy of the block's state. |
instances examples
// Read the third row's input value {{ my_repeatable.instances[2].price_input.value }} // Count how many rows have a checkbox checked {{ my_repeatable.instances.filter(row => row.done_checkbox.value).length }} // Get all selected row IDs {{ my_repeatable.instances .filter(row => row.select_checkbox.value) .map(row => row.currentPrimaryKey) }}
Rules for instances
| Rule | Details |
| Direct children only | A nested Repeatable inside another doesn't flatten — chain: {{ my_repeatable.instances[0].inner_repeatable.instances[1] }}. |
| Read-only | Mutating array methods (push, sort, reverse) are blocked. Copy first: {{ [...my_repeatable.instances].sort(byTotal) }}. |
| Unknown key = empty + console warning | Reading a key that isn't currentIndex, currentPrimaryKey, or a direct child block's name produces an empty value and logs a one-time warning. |
| Untouched rows report template state | A row the user hasn't interacted with answers with the template defaults, not a recorded per-row edit. |
Legacy: items
Older pages may bind {{ my_repeatable.items[0] }} — this still resolves, but instances is the current form and what autocomplete describes. Prefer instances for any new bindings.
Common Patterns
Pattern — Row status badge color
// In a Table column expression: {{ currentRow.status === 'open' ? '#22c55e' : currentRow.status === 'overdue' ? '#ef4444' : '#6b7280' }}
Pattern — Conditional action per row
// Button visibility inside a Repeatable row: visible: {{ my_repeatable.context.item.status !== 'closed' }} // The button's action then uses this row's ID: Navigate to: /orders/{{ my_repeatable.context.item.id }}
Pattern — Select all / count checked
// Count selected rows (checkbox named row_check in each row): {{ my_repeatable.instances.filter(r => r.row_check.value).length }} selected // Show "Select all" button only if not all are checked: visible: {{ my_repeatable.instances.some(r => !r.row_check.value) }}
Gotchas
| Gotcha | Details |
| Table cell utils is not the standard utils | Inside Table column value expressions, utils is a different, smaller set. Standard helpers like utils.formatDate are absent there. |
| currentRow/currentIndex/currentPrimaryKey are not reserved for entity names | The builder won't stop you from naming an entity currentIndex. Inside a Repeatable or Table, the row-scoped name wins — your entity becomes unreachable there. Avoid all three names for entities. |
| Nested Repeatables don't flatten | instances of an inner Repeatable live under the outer row's instance — you must chain, not access the inner directly from the outer. |
| Builder always previews row 1 | Whatever row you clicked to edit, the expression editor shows row 1's values. Test with different data by varying the data source's first record. |
Frequently Asked Questions
Why does formatting with utils.formatDate show nothing in a Table cell?
Inside Table column value expressions, the utils namespace is a smaller, table-specific set — the standard helpers like utils.formatDate are not available there. Format dates and numbers either in the data source (via a response transform), in a page function, or use the Table column's built-in formatting options in the column configuration panel.
How do I access the current row's ID in a button action inside a Repeatable?
Reference {{ my_repeatable.context.item.id }} (where id is the field holding the row's identifier). Inside the row, sibling blocks and the row context are scoped to that row — the action uses that row's values. You can also use {{ my_repeatable.context.primaryKey }} if the platform has set the identity key for you.
Can I read values from all rows at once, not just the current one?
Yes — use {{ my_repeatable.instances }} from anywhere on the page. This is an array with one entry per row, each holding the row's index, primary key, and all direct child block states. You can .filter(), .map(), or .reduce() over it to aggregate values across all rows, such as summing a column or counting checked rows.
Why does my Repeatable show the same value in every row?
You may be referencing the page-level name instead of the row-scoped context. For example, {{ title_var.value }} always reads the page variable, while {{ my_repeatable.context.item.title }} reads each row's own title. Ensure your binding goes through my_repeatable.context.item rather than a page-level entity.