Overview
The evaluateJavaScript action (also referenced as eval-js in the builder) runs arbitrary JavaScript code and captures its return value. It is the escape hatch for computation that cannot be expressed in UnifyApps' template binding syntax — multi-step transformations, recursive operations, third-party algorithm logic, and anything requiring Array.reduce, Map/Set operations, or complex conditional logic.
Sandbox Environment
The expression runs in a restricted JavaScript sandbox with the following characteristics:
No DOM access —
document,window, and browser APIs are not available.No network access —
fetch,XMLHttpRequest, andWebSocketare blocked.No timers —
setTimeout,setInterval, andrequestAnimationFrameare not available. Use a Delay action in the chain instead.Standard library available —
JSON,Math,Date,Array,Object,String,RegExp,Map,Set, and other built-in objects work normally.Page scope available (when
scope: "page") —thisrefers to a read-only snapshot of the current page's variable and data source state.Execution timeout: 5 seconds — expressions that run longer are aborted and the
onErrorcallback fires.
Warning: Avoid infinite loops. The sandbox enforces a 5-second execution timeout, but tightly looping code can freeze the UI thread for that duration. Test expressions with representative data volumes before using them in production.
Parameters
| Parameter | Type | Required | Default | Description |
| expression | string | Required (required) | — | The JavaScript code to evaluate. The last expression's value (or an explicit return statement in a function body) is captured as the result. Bindings ({{ }}) in this field are resolved before the expression is evaluated, allowing you to inject variable values as literals. |
| scope | "page" | "global" | Optional (optional) | "page" | page — this is a read-only snapshot of the page's current state (variables, block states, data source responses). global — this is the global app state (app variables, user profile, app metadata). Use page for most computations; global for cross-page utilities. |
| outputVariable | string | Optional (optional) | — | The ID of the page variable to store the expression's return value in. If omitted, the result is discarded. The variable must already exist — use a Set Page Variable action to create it first if needed. |
Accessing Page State in Expressions
When scope is "page", the expression context (this) exposes:
| Path | Description |
| this.variables | All page variables as a key-value object. |
| this.dataSources | All data source responses indexed by ID. |
| this.blocks | Block state snapshots indexed by block ID. |
| this.appUser | The currently authenticated user object. |
Alternatively, use bindings to inject values directly into the expression string — they are resolved as JSON literals before the JS engine sees the code:
Injecting a variable value via binding
// expression field: const items = {{ var_cartItems }}; const total = items.reduce((sum, item) => sum + item.price * item.qty, 0); total.toFixed(2)
Step-by-Step Usage Guide
Identify the computation needed: Determine what value you need to compute and which variables or data source fields it depends on. Check whether it can be expressed in a binding expression first —
evaluateJavaScriptis best reserved for multi-step logic that would be unreadable as a one-liner binding.Create an output variable: In the Page Variables panel, create a variable to hold the result (e.g.
var_computedTotal). Set its initial value to a sensible default (e.g.0for a number,""for a string).Write the expression: In the action editor, enter your JavaScript. The value of the final expression (or a
returnstatement) is used as the result. Multi-line expressions are supported using a code block that begins with(function() {and ends with})().Set outputVariable: Set
outputVariableto your variable's ID (e.g.var_computedTotal). After the action runs, the variable is updated and any bindings referencing it re-render automatically.
Examples
Calculate a cart total with tax
{ "actionType": "evaluateJavaScript", "payload": { "expression": "(function() {\n const items = {{ var_cartItems }};\n const subtotal = items.reduce((s, i) => s + i.price * i.qty, 0);\n const tax = subtotal * 0.18;\n return (subtotal + tax).toFixed(2);\n})()", "outputVariable": "var_cartTotal", "scope": "page" } }
Generate a slug from a title string
{ "actionType": "evaluateJavaScript", "payload": { "expression": "'{{ var_articleTitle }}'.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')", "outputVariable": "var_slug", "scope": "page" } }
Parse and reformat a date string
{ "actionType": "evaluateJavaScript", "payload": { "expression": "(function() {\n const d = new Date('{{ var_rawDate }}');\n if (isNaN(d.getTime())) return 'Invalid date';\n return d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' });\n})()", "outputVariable": "var_formattedDate", "scope": "page" } }
Note: When to use Page Functions instead: For reusable logic that needs to be called from multiple places, define a Page Function instead of duplicating evaluateJavaScript actions. Page Functions are named, versioned, and testable in isolation.