Unify Logo Footer.svg
Unify Applications
Logo
Row-scoped Bindings

Row-scoped Bindings

Logo

6 mins READ

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.

NameResolves toExample
currentRowThe full data record for this row.{{ currentRow.status }}
currentRow.fieldAny 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:

BindingResolves 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 instanceDescription
currentIndexThe row's position (0-based).
currentPrimaryKeyThe 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

RuleDetails
Direct children onlyA nested Repeatable inside another doesn't flatten — chain: {{ my_repeatable.instances[0].inner_repeatable.instances[1] }}.
Read-onlyMutating array methods (push, sort, reverse) are blocked. Copy first: {{ [...my_repeatable.instances].sort(byTotal) }}.
Unknown key = empty + console warningReading 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 stateA 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

GotchaDetails
Table cell utils is not the standard utilsInside 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 namesThe 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 flatteninstances 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 1Whatever 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.