Unify Applications
Logo
Binding Context

Binding Context

Logo

5 mins READ

Every expression on a page evaluates against one shared namespace — a picture of everything the page knows at that moment. Blocks, data sources, page variables, and page functions sit at the top level under their own names; everything else lives under a fixed, well-known name like userContext or theme. When any part of this picture changes, every expression that reads it recomputes.

binding_context.png
binding_context.png

Page Entities (Named by You)

Blocks, data sources, page variables, and page functions are spread at the top level of the context under their names. You reference them directly — no namespace prefix required.

Entity typeWhat it exposesExample expressions
Block (by its name)The block's exposed state — its value, selection, loading flags — plus any methods it registers. Call a state-changing method such as submitForm() only from an action or a script, never in a rendered binding: bindings re-evaluate on every change and would call it again each time.{{ email_input.value }} {{ orders_table.selectedRow.id }} {{ my_form.submitForm() }}
Data source (by its name)data (transformed result), rawData (untransformed), isLoading, isFetching, error, inputs. A trigger() method re-runs it on demand. For infinite sources: also hasNextPage and isFetchingNextPage.{{ orders_query.data.objects }} {{ orders_query.isLoading }} {{ orders_query.error?.message }}
Page variable (by its name){ value, setValue } — the bare name is the whole object. Read .value for the current value; call .setValue(x) to update it (from an action or a script only).{{ selected_tab.value }} {{ selected_tab.setValue('billing') }}
Page function (by its name){ value, error, isEvaluated } — the computed result is at .value. A manual function also has a .trigger() method.{{ format_title.value }} {{ format_title.trigger() }}

Warning: Writing {{ selected_tab }} returns the object { value, setValue }, which renders as [object Object] in a text block. Always append .value: {{ selected_tab.value }}.

Fixed Namespaces (Always Available)

These names are always in scope and do not depend on what blocks or data sources you've added to the page.

Namespace

What it holds

Example

userContext

The signed-in user: id, email, name, username, locale, roles, plus any org-specific attributes under userContext.customProperties. userContext.context is a legacy view of device and network state — use deviceContext instead.

{{ userContext.email }}

pageInputs

Values the page declares as inputs — read from the URL query string (e.g. ?customerId=42), or handed in by a navigation action, a parent page, or an embed. Values arrive converted to the types the page declares. Undeclared query parameters are not read.

{{ pageInputs.customerId }}

pageParams

Named path segments from the page's route. The orderId in /orders/:orderId is here — not in pageInputs, and not in the query string.

{{ pageParams.orderId }}

location

The current URL broken up: href, hostname, pathname, search (raw query string), hash, pathParams (same as pageParams).

{{ location.pathname }} {{ location.search }}

theme

Active appearance: colorScheme ('light' or 'dark'), activeTheme, availableThemes.

{{ theme.colorScheme === 'dark' ? '/logo-dark.svg' : '/logo-light.svg' }}

utils

Built-in helpers for dates, numbers, JSON, and downloads. See Built-in Utilities.

{{ utils.formatDate(order.createdAt, 'dd MMM yyyy') }}

envVariables

Environment variables configured for the app.

{{ envVariables.API_BASE_URL }}

localStorage

Values stored in browser localStorage. Has a setValue(key, val) method and a clear() method.

{{ localStorage.draftFilters }}

sessionStorage

Values stored in browser sessionStorage. Has a clear() method. Note: setValue() on sessionStorage does not persist — use the Set Interface Session Storage action instead.

{{ sessionStorage.tempToken }}

interfaceRecord

The app itself: id, name, logo metadata, and navigation structure.

{{ interfaceRecord.name }}

mediaUploads

Files selected in file-upload blocks, keyed by block name.

{{ mediaUploads.receipt_upload }}

appsAndIntegrations

Results from platform integrations (e.g. payment confirmation details from Razorpay).

{{ appsAndIntegrations.razorpay.paymentId }}

actions

Callable utilities — same as the visual actions, invokable from any expression or script: showNotification, logToDebugger, navigateToPage, emitPageEvent, and setURLParameters. emitPageEvent and setURLParameters (and navigateToPage) work only in page-level scripts, not app-level ones.

{{ actions.showNotification({ title: 'Saved' }) }}

deviceContext

Device state. On all apps: deviceInfo, network, location. Web apps add browser. Mobile apps add permissions, biometric, safeArea, buildInfo, and offlineMode.

{{ deviceContext.permissions.camera }}

Reading a Value from the URL

Three namespaces look similar but come from different sources:

Namespace

What it holds

Example

pageParams.orderId

Named path segments — the orderId in /orders/:orderId. Same value as location.pathParams.orderId.

For route-based IDs like /customers/42

pageInputs.customerId

Inputs the page declares. Read from the URL query string (e.g. ?customerId=42) or handed in by a navigation action, a parent page, or an embed. Undeclared query parameters are not read.

For URL query parameters — declare them as page inputs first

location.search

The raw query string: ?status=open&page=2. Parse it yourself for specific keys.

For raw parsing of query parameters the page doesn't declare

Row-scoped Names

Rows have their own names, and Tables and Repeatables reach them differently:

  • Table column expressions: currentRow is the record behind the row being rendered, e.g. {{ currentRow.status }}.

  • Blocks inside a Table row: reach the row through the Table's name, e.g. {{ my_table.context.currentRow }}.

  • Blocks inside a Repeatable row: use the Repeatable's name: {{ my_repeatable.context.item }} (the row's data), {{ my_repeatable.context.index }} (its position, from 0) and {{ my_repeatable.context.primaryKey }} (its identity key).

  • Across rows: currentIndex and currentPrimaryKey are keys on each entry of a Repeatable's instances array, e.g. {{ my_repeatable.instances[2].currentIndex }}. They are not names in scope on their own.

See Row-scoped Bindings for the full story.

Reserved Names

Some names are reserved — the builder rejects them for blocks, data sources, variables, and functions so they can never shadow something a binding expects. The full blocked set:

  • Fixed namespace names: location, utils, lib, theme, userContext, deviceContext, pageInputs, pageParams, envVariables, localStorage, sessionStorage, interfaceRecord, mediaUploads, appsAndIntegrations, actions, dataSources, dataSourcesMeta, runtimeBlocks, queryMeta, walkthrough, serverUtils

  • Per-entity member names: value, instances, context, item, inputs

  • Browser globals: window, document, alert, console, navigator, location, history, screen, performance, fetch, crypto, localStorage, sessionStorage, XMLHttpRequest, Event, Node, Element, HTMLElement, CSS, CSSStyleDeclaration, matchMedia, setTimeout, clearTimeout, setInterval, clearInterval, requestAnimationFrame, cancelAnimationFrame, addEventListener, removeEventListener, dispatchEvent

  • Common built-ins: Array, Object, String, Number, Boolean, Date, Math, JSON, Promise, Map, Set, WeakMap, WeakSet, Error, RegExp, Function, Symbol, Proxy, Reflect, Intl, BigInt, globalThis, undefined, NaN, Infinity, isNaN, isFinite, parseFloat, parseInt, encodeURI, decodeURI, encodeURIComponent, decodeURIComponent, eval

  • JavaScript keywords: break, case, catch, class, const, continue, debugger, default, delete, do, else, enum, export, extends, false, finally, for, function, if, import, in, instanceof, new, null, return, super, switch, this, throw, true, try, typeof, var, void, while, with

  • SQL keywords: select, from, where, insert, update, delete, join, create, alter, drop, table, index, view, procedure, trigger, function, database, schema, grant, revoke, commit, rollback, savepoint, transaction, with, as, and, or, not, between, in, like, is, null, distinct, group, by, having, order, asc, desc, limit, offset, union, all, intersect, except, case, when, then, else, end, exists, for, some, any, values, into, on, inner, outer, left, right, full, cross, natural, using

  • Groovy and Java keywords (in addition to the JavaScript ones): as, assert, def, goto, implements, interface, package, throws, trait, abstract, final, native, private, protected, public, static, strictfp, synchronized, transient, volatile

The check ignores case, so Theme and THEME are blocked too. currentRow is also reserved inside expressions.

Scope Across Embedded Pages

An embedded page — a module, a template component, or a copilot thread — evaluates against its own blocks, pageInputs, and pageParams, separate from the page hosting it. App-wide values stay shared: userContext, theme, envVariables, location, and browser storage are available everywhere.

Modals, Drawers, and Bottom Sheets do not get their own namespace — they are blocks on the page and share the same context as everything else on it.

FAQs

How do I get the logged-in user's email?

{{ userContext.email }} // Also available: userContext.id, userContext.name, userContext.locale // Org-specific attributes: {{ userContext.customProperties.department }}

How do I check if the app is in dark mode?

{{ theme.colorScheme === 'dark' }} returns true in dark mode. Use it in a ternary: {{ theme.colorScheme === 'dark' ? '/logo-dark.svg' : '/logo-light.svg' }}.

Why does my page variable show [object Object]?

// Wrong — the bare name is { value, setValue } {{ my_var }} // Correct — append .value {{ my_var.value }}

How do I emit a page event from inside a script?

{{ actions.emitPageEvent({ name: 'orderPlaced', payload: { id: order.id } }) }} {{ actions.showNotification({ title: 'Order saved', type: 'success' }) }}