Unify Logo Footer.svg
Unify Applications
Logo
Variables Overview

Variables Overview

Logo

7 mins READ

What Is a Variable?

A variable is a named piece of in-memory state you define once and bind anywhere in your app — a counter, a selected record, a draft object, a feature flag. Instead of wiring values block-to-block, you write to the variable in one place and every block, expression, or action input that reads it updates automatically.

Every variable has three attributes:

  • Name — a human label shown in the builder.

  • Type — one of five: string, number, boolean, object, or array. The type decides which write operations are available.

  • Initial Value — the value the variable starts with. Can be a fixed literal or a {{ }} expression evaluated at page load.

Variable Types

TypeStoresAvailable Write Operations
stringText valuesSET, APPEND (add to end), PREPEND (add to beginning)
numberNumeric valuesSET, INCREMENT_BY, DECREMENT_BY
booleanTrue/false flagsSET, TOGGLE
objectKey-value maps (JSON objects)SET, MERGE (shallow), REMOVE_PROPERTY
arrayOrdered listsSET, APPEND, PREPEND, REMOVE_LAST, REMOVE_FIRST, REMOVE_AT, REMOVE_BY_VALUE

Two Scopes: Page and App

Variables exist at two scopes:

ScopeVisibilityResets WhenUse For
Page variableBelongs to a single page — invisible to other pagesNavigating away from the page or reloading the appUI state, selections, form drafts within a page
App variableAvailable on every page in the appFull browser reload or opening a new tabState that must survive in-app navigation: selected workspace, global filter

Note: App variables are page variables on the global page. Internally, an app variable is just a page variable declared on the app's global page entity. The same types, operations, and reactivity apply — only the scope and lifetime differ.

Reading a Variable

Variables are read through {{ }} binding expressions. The binding scope exposes each variable under its ID (not its name), with the current value nested under ['value']:

Reading a variable in a binding

{{ var_abc['value'] }} // read by variable ID {{ var_cartCount['value'] }} // a number variable {{ var_filters['value'] }} // an object variable

Warning: Always read by ID, not name. The binding scope is keyed by variable ID (var_abc), not by the human-readable name. Reading {{ myVariableName['value'] }} will produce an empty result — use the ID from the builder's variable panel.

Writing a Variable

You never assign to a variable directly. Writes always go through the Set page variable action (actionType: "setPageVariable"), which applies a typed operation. You can also call setValue directly from a script or function — for example {{ var_abc.setValue('newValue') }}.

Set page variable action — toggle a boolean

{ "actionType": "setPageVariable", "payload": { "variableId": "var_showSidebar", "operation": "TOGGLE" } }

Reactivity

Variables are reactive. When a write lands, everything bound to that variable — block properties, visibility conditions, expressions, action inputs — re-computes immediately and the affected blocks re-render. You never refresh a reader manually; binding to the variable is the subscription.

After each write the stored value is frozen, so nothing downstream can quietly mutate it in place. A fresh write is always required to change it. This keeps reactivity honest — readers can trust that a value only changes when a write fires.

Initial Values

A variable's initial value is always stored as a string and evaluated once when the page opens. It can be:

  • A fixed literal converted to the declared type ("false" for boolean, "42" for number, "{'status':'new'}" for object).

  • A {{ }} expression evaluated against the page's load-time context. The result is coerced to the declared type — a wrong-type result becomes undefined.

Warning: Initial value is a starting point, not a live binding. It is computed once at page load. The variable does not keep following the bound value afterwards — later changes come only through explicit writes (the action or setValue).

Persistence and Lifetime

Variable state lives only in memory. When a value must survive beyond the variable's lifetime, pair it with a storage action:

ScenarioSolution
Value must survive navigating away and back to the same pagePromote to an app variable (app scope)
Value must survive a full page reload (same browser tab)Use setInterfaceSessionStorage action + read from session storage binding
Value must survive indefinitely across sessions and reloadsUse setLocalStorage action + read from local storage binding
Value must be shareable via URL (bookmarkable)Use setURLParameters action + read from {{ pageInputs['key'] }}

Scope and Reachability

The binding context determines which variables you can see:

  • Page-level bindings (blocks, functions, action inputs on a page) can read both the page's own variables AND app/global variables.

  • App-level bindings (app-level data sources, global page's own variables) can only read app/global variables — a specific page's variables are not visible there.

Note: If a value must be read from an app-level data source or from a different page, declare it as an app variable (on the global page) — not a page variable.

Variables vs. Functions

Use a Variable when…Use a Function when…
The value is set by user actions (clicks, form inputs, events)The value is derived from other state (computed totals, filtered lists, formatted labels)
You need to store something that events write and blocks readYou want a value that always stays current with its inputs automatically
The value needs type-specific operations (increment, toggle, append)The computation involves logic too complex for a single binding expression

Common Gotchas

Page variables don't cross pages

A page variable set on page A doesn't exist on page B — navigating loses it. When two pages need the same value, promote it to an app variable or pass it through URL parameters.

Must read by ID, not name

Renaming a variable does not change its ID. Id-based bindings survive a rename — but name-based reads (which the platform does not support) would break. Always use the ID from the builder.

A write to a deleted variable silently does nothing

If a write targets a variable that no longer exists, the write is skipped without any visible error. Renaming or deleting a variable while actions still point at the old ID quietly breaks those actions.

MERGE is shallow

For object variables, the MERGE operation is shallow — a nested object is replaced outright, not deep-merged. To update a deeply nested field, use a full SET with the complete updated value.

Frequently Asked Questions

Can I read one page's variable from a different page?

No. Page variables are scoped to the page they are declared on. Another page cannot read them. If you need to share state between pages, declare the variable at app scope (on the global page) instead.

What happens to a page variable when I navigate away and back?

It resets to its initial value. Page variables live in memory only and are torn down when the visitor leaves the page. For cross-navigation persistence, use an app variable, session storage (setInterfaceSessionStorage), or local storage (setLocalStorage).

Can I use a variable as a filter for a data source?

Yes — bind the variable's value into the data source's filter or parameter field: {{ var_statusFilter['value'] }}. Whenever the variable changes the data source re-evaluates its inputs and re-triggers automatically.

What is the difference between MERGE and SET for an object variable?

MERGE does a shallow merge — only the top-level keys you provide are updated; other existing keys are left intact. SET replaces the entire object. Use MERGE to patch one field without overwriting the rest; use SET when you have the complete updated object ready.

How do I share a value between a page and its modals?

Modals open on the same page, so the page's own variables are fully accessible inside them. Declare the variable on the page, write to it before opening the modal, and bind to it inside the modal's blocks — it works without any special setup.