Widget Visibility
Widget-level visibility lets you show different content to different roles on the same page — hide edit buttons from viewers, show admin panels only to admins, or display role-specific data sections. Hidden widgets are not rendered at all, leaving no empty space.
visibleTo — Simple Role List
The visibleTo property accepts a list of roles. The widget renders only if the user has at least one of the listed roles:
// Widget placement config
{
"widgetId": "delete-lead-button",
"type": "Button",
"config": {
"label": "Delete Lead",
"variant": "danger",
"action": { "type": "trigger-workflow", "workflowId": "DeleteLead" }
},
"visibleTo": ["admin", "sales-manager"] // hidden from sales, viewer, etc.
}
visibilityExpression — Full Expression Power
For complex conditions, use visibilityExpression with a token expression that evaluates to a boolean:
// Visible only when the user is an admin AND the lead is in "qualified" status
{
"widgetId": "qualify-action-panel",
"type": "ActionPanel",
"visibilityExpression": "{{ context.roles.includes('admin') && row.status === 'qualified' }}"
}
// Visible to admin OR when the user is the assigned owner of this record
{
"widgetId": "edit-button",
"type": "Button",
"visibilityExpression": "{{ context.roles.includes('admin') || row.ownerId === context.userId }}"
}
visibleTo vs. visibilityExpression
| Property | Syntax | Use when |
|---|---|---|
visibleTo | Array of role strings | Simple role whitelist (OR logic among listed roles) |
visibilityExpression | Token expression → boolean | AND logic, data-conditional, user-ownership checks, complex rules |
If both are set, the widget is visible only when both conditions are true.
Hidden vs. Disabled
App Studio widget visibility always means hidden — the widget is not rendered at all. There is no built-in "render but disable based on role" behavior. To show a widget in a disabled state for certain roles, use two widget instances:
// Pattern: show editable for editors, read-only for viewers
// Widget A — editable version, shown to editors
{
"widgetId": "name-field-edit",
"type": "TextInput",
"visibleTo": ["editor", "admin"]
}
// Widget B — read-only version, shown to viewers
{
"widgetId": "name-field-readonly",
"type": "Text",
"config": { "value": "{{ variables.leadName }}" },
"visibilityExpression": "{{ !context.roles.includes('editor') && !context.roles.includes('admin') }}"
}
Practical Examples
// Admin-only section header
{
"widgetId": "admin-section-label",
"type": "SectionHeader",
"config": { "label": "Admin Controls" },
"visibleTo": ["admin"]
}
// Approval button — only for users with 'approver' role
{
"widgetId": "approve-btn",
"type": "Button",
"config": { "label": "Approve", "action": { "type": "trigger-workflow", "workflowId": "ApproveRecord" } },
"visibleTo": ["approver", "admin"]
}
// Revenue column in data grid — only for managers
{
"widgetId": "leads-grid",
"type": "DataGrid",
"config": {
"columns": [
{ "field": "name", "header": "Name" },
{ "field": "status", "header": "Status" },
{
"field": "dealValue",
"header": "Deal Value",
"visibleTo": ["manager", "admin"] // Column-level visibility
}
]
}
}