App Studio
Role-Based Expression
Token expressions evaluate against the live context object to produce visibility decisions. The full JavaScript expression syntax is available — roles, user identity, data values, and logical operators can all be combined in a single expression.
context.roles Available Expressions
// Basic role check
{{ context.roles.includes('admin') }}
// OR — user has sales OR manager role
{{ context.roles.includes('sales') || context.roles.includes('manager') }}
// AND — user must be both admin AND regional-lead
{{ context.roles.includes('admin') && context.roles.includes('regional-lead') }}
// NOT — visible to everyone except viewers
{{ !context.roles.includes('viewer') }}
// Some — user has at least one of several roles (array intersection)
{{ ['admin', 'manager', 'supervisor'].some(r => context.roles.includes(r)) }}
Combining Role Checks with Data Values
// Button visible to admins, OR to the record's owner
{{ context.roles.includes('admin') || row.assignedUserId === context.userId }}
// Panel visible only when user is admin AND record is in 'review' status
{{ context.roles.includes('admin') && variables.currentStatus === 'review' }}
// Action visible during business hours for managers only
{{ context.roles.includes('manager') && new Date().getHours() >= 9 && new Date().getHours() < 17 }}
context Object Reference
| Token | Type | Description |
|---|---|---|
context.userId | string | Authenticated user's unique ID from Passport |
context.tenantId | string | The tenant this session belongs to |
context.roles | string[] | Array of role names granted to this user in this tenant |
context.displayName | string | User's display name for UI greetings |
context.email | string | User's email address |
context.claims | object | Full set of custom claims from the Passport JWT |
Using visibilityExpression on Different Elements
// AppPage — hides the entire page from non-admins
{
"pageId": "admin-panel",
"visibilityExpression": "{{ context.roles.includes('admin') }}"
}
// NavItem in sidebar
{
"type": "item",
"label": "Admin Panel",
"targetPageId": "admin-panel",
"visibilityExpression": "{{ context.roles.includes('admin') }}"
}
// Widget placement
{
"widgetId": "delete-button",
"type": "Button",
"visibilityExpression": "{{ context.roles.includes('admin') }}"
}
// DataGrid column
{
"field": "salary",
"header": "Salary",
"visibilityExpression": "{{ context.roles.includes('hr') || context.roles.includes('admin') }}"
}
Expression Evaluation Rules
- Expressions must return a boolean (truthy/falsy values are coerced)
- Expressions are evaluated at render time, not at compile time
- If an expression throws an error, the widget defaults to hidden (fail-closed)
- Expressions have access to:
context,variables,route,row(inside grids),modal(inside modals) - Do not make async calls inside visibility expressions — they are synchronous
Fail-closed on error
If a visibility expression evaluates to an error (e.g., accessing a property on undefined), the widget is hidden rather than shown. This is the safe default — always test expressions with the State Inspector before publishing.