Skip to content

App Builder Expressions

Expression syntax reference for dynamic content

Expressions use {{ }} syntax to inject dynamic values into component properties. They enable:

  • Displaying user data
  • Accessing workflow results
  • Reading form field values
  • Conditional logic for visibility/disabled states
  • Dynamic navigation paths

Returns the raw value (string, number, boolean, object):

{{ user.name }} // "John"
{{ variables.count }} // 42
{{ user.role == 'admin' }} // true
{{ data.items }} // [...]

Text with embedded expressions returns a string:

"Hello, {{ user.name }}!" // "Hello, John!"
"Items: {{ variables.count }}" // "Items: 42"
"/users/{{ row.id }}/edit" // "/users/abc123/edit"

Expressions have access to these context variables:

Current authenticated user:

{{ user.id }} // User UUID
{{ user.name }} // Display name
{{ user.email }} // Email address
{{ user.role }} // Role string

Page-level mutable state:

{{ variables.selectedId }}
{{ variables.filterText }}
{{ variables.currentTab }}

Set variables using the set-variable action.

Data from page data sources:

{{ data.customers }} // Array from "customers" data source
{{ data.stats.totalRevenue }} // Nested property
{{ data.users.0.name }} // Array index access

Form input values (by fieldId):

{{ field.customerName }}
{{ field.quantity }}
{{ field.isActive }}

Last workflow execution result:

{{ workflow.executionId }} // UUID of execution
{{ workflow.status }} // "pending" | "running" | "completed" | "failed"
{{ workflow.result }} // Workflow return value
{{ workflow.result.orderId }} // Nested result property
{{ workflow.error }} // Error message if failed

Current row in table actions:

{{ row.id }}
{{ row.name }}
{{ row.user.email }} // Nested property
{{ user.role == 'admin' }} // Equals
{{ user.role === 'admin' }} // Strict equals
{{ user.role != 'admin' }} // Not equals
{{ user.role !== 'admin' }} // Strict not equals
{{ variables.count > 10 }} // Greater than
{{ variables.count >= 10 }} // Greater than or equal
{{ variables.count < 10 }} // Less than
{{ variables.count <= 10 }} // Less than or equal
{{ user.isActive && user.isVerified }} // AND
{{ user.isAdmin || user.isManager }} // OR
{{ !user.isBlocked }} // NOT
{{ (user.role == 'admin' || user.role == 'manager') && user.isActive }}
{{ true }}
{{ false }}
{{ null }}
{{ undefined }}
{{ 42 }}
{{ 3.14 }}
{{ 'single quotes' }}
{{ "double quotes" }}
{{ user.profile.name }}
{{ data.orders.0.items.0.name }}

Access deeply nested data safely:

{{ data.customer.address.city }} // Returns undefined if any level is missing
{{ workflow.result.data.id }}

Show component only for admins:

{
"type": "button",
"visible": "{{ user.role == 'admin' }}",
"props": { "label": "Admin Action" }
}

Disable button while workflow runs:

{
"type": "button",
"props": {
"label": "Submit",
"disabled": "{{ workflow.status == 'running' }}"
}
}

Disable based on form state:

{
"type": "button",
"props": {
"label": "Save",
"disabled": "{{ !field.name || !field.email }}"
}
}

Navigate with row data:

{
"onClick": {
"type": "navigate",
"navigateTo": "/customers/{{ row.id }}"
}
}

Navigate with variables:

{
"onClick": {
"type": "navigate",
"navigateTo": "/search?q={{ variables.searchTerm }}"
}
}

Pass expressions to workflows:

{
"actionType": "workflow",
"workflowId": "update_customer",
"actionParams": {
"id": "{{ variables.customerId }}",
"name": "{{ field.customerName }}",
"email": "{{ field.customerEmail }}"
}
}

Template interpolation:

{
"type": "text",
"props": {
"text": "Welcome back, {{ user.name }}! You have {{ data.notifications.length }} new notifications."
}
}
{
"type": "data-table",
"props": {
"columns": [
{
"key": "status",
"header": "Status",
"type": "badge",
"badgeColors": {
"active": "green",
"pending": "yellow",
"inactive": "gray"
}
}
]
}
}

Show action only for certain rows:

{
"rowActions": [
{
"label": "Approve",
"visible": "{{ row.status == 'pending' }}",
"onClick": {
"type": "workflow",
"workflowId": "approve_item"
}
}
]
}

Disable action for locked rows:

{
"rowActions": [
{
"label": "Delete",
"disabled": "{{ row.isLocked || row.status == 'active' }}",
"onClick": { "type": "workflow", "workflowId": "delete_item" }
}
]
}

Save workflow result to variable:

{
"onComplete": [
{
"type": "set-variable",
"variableName": "lastResult",
"variableValue": "{{ workflow.result }}"
}
]
}
  • Expressions in boolean contexts (visible, disabled) are coerced to boolean
  • Empty strings, null, undefined, 0 are falsy
  • Non-empty strings, objects, arrays, non-zero numbers are truthy
  • Accessing undefined properties returns undefined
  • undefined in templates renders as empty string
  • undefined in comparisons should use explicit checks
// Safe pattern for optional values
{{ user.middleName || '' }}
{{ variables.count || 0 }}
{{ data.items.0 }} // First item
{{ data.items.0.name }} // First item's name property

Expressions do not support:

  • Function calls ({{ items.filter(...) }})
  • Complex arithmetic beyond comparisons
  • Array methods (map, filter, reduce)
  • String methods (toLowerCase(), split())
  • Ternary operators ({{ x ? y : z }}) - use logical OR instead
  • Object destructuring

For complex transformations, compute values in your workflow and return them in the result.

If expressions don’t evaluate as expected:

  1. Check property names match exactly (case-sensitive)
  2. Verify data source is loaded before component renders
  3. Check for typos in context variable names (user not users)
  4. Use browser dev tools to inspect rendered values