Unify Applications
Logo
Row-scoped Bindings

Row-scoped Bindings

Logo

6 mins READ

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.

row_scoped_bindings.png
row_scoped_bindings.png

Inside a Table: context.currentRow

Inside a Table, the row being rendered is available under the Table's own block ID. In a column's Value, in a per-row condition (such as an Editable condition) and in blocks placed inside a cell, {{ my_table.context.currentRow }} is the record behind the row. There is no bare currentRow for Tables: always start with the Table's block ID. In the examples below, my_table is the Table's ID.

Binding

Resolves to

Example

my_table.context.currentRow

The full data record for this row.

{{ my_table.context.currentRow.status }}

my_table.context.currentRow.field

Any field from this row's data object.

{{ my_table.context.currentRow.qty * my_table.context.currentRow.price }}

my_table.context.currentRowIndex

This row's position, starting at 0.

{{ my_table.context.currentRowIndex === 0 }}

Table cell expression examples

// A field from this row {{ my_table.context.currentRow.status }} // Computed per row {{ my_table.context.currentRow.qty * my_table.context.currentRow.price }} // Conditional per row {{ my_table.context.currentRow.status === 'overdue' ? 'red' : 'green' }} // Optional chaining for nullable fields {{ my_table.context.currentRow.customer?.name ?? 'Unknown' }}

Note: Table cell expressions get the standard utils helpers plus three table helpers, all called under utils. For example, {{ utils.formatDate(my_table.context.currentRow.createdAt, 'dd MMM yyyy') }} works in a cell.

Helper

What it does

Example

utils.filesize(bytes)

Turns a size in bytes into readable text.

{{ utils.filesize(my_table.context.currentRow.sizeBytes) }} → 1.5 MB

utils.formatTime(ms, precision, notation, maxSignificantUnits)

The same duration formatter as on the Built-in Utilities page.

{{ utils.formatTime(my_table.context.currentRow.durationMs, 'seconds') }} → 1d 1h 1m 1s

utils.hasGlobalAccess(entityType, permission)

Returns true when the signed-in user has that permission on that type of asset.

{{ utils.hasGlobalAccess('WORKFLOW_DEFINITION', 'EXECUTE') }}

Blocks Inside a Table Row

Blocks placed inside a Table cell, such as the blocks in a Custom column's Custom Cell slot, use the same binding. Each row's copy of the blocks gets that row's data:

{{ my_table.context.currentRow.status }}

Outside the Table, read the row the user selected through the Table's state, for example {{ my_table.selectedRow }} when Row Selection is set to Single. See Table State and Bindings.

Inside a Repeatable: Row Context

Repeatables have no bare currentRow, currentIndex or currentPrimaryKey. Use the Repeatable's own name, as below. currentIndex and currentPrimaryKey exist only as keys on instances.

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((a, b) => a.total_input.value - b.total_input.value) }}.
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: {{ my_table.context.currentRow.status === 'open' ? '#22c55e' : my_table.context.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

No bare currentRow in Tables

{{ currentRow }} on its own doesn't resolve. Start with the Table's block ID, for example {{ my_table.context.currentRow.status }}. These bindings work only inside the Table: from a block elsewhere on the page they're empty, so read {{ my_table.selectedRow }} instead.

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. To check other rows, open the page in Preview, where every row renders with its own data.

FAQs

Can I use utils.formatDate in a Table cell?

Yes. Table cell expressions get the standard utils helpers plus table helpers (filesize, formatTime, hasGlobalAccess), so {{ utils.formatDate(my_table.context.currentRow.createdAt, 'dd MMM yyyy') }} works in a cell (my_table is your Table's block ID). You can also 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.