Page Visibility
Each AppPage can be restricted to specific roles. Pages the user doesn't qualify for are removed from the sidebar navigation and return 403 if accessed directly via URL — a clean, role-aware navigation experience with no configuration in the navigation widget itself.
AppPage requiredRoles
// AppPage configuration
{
"pageId": "admin-settings",
"title": "Admin Settings",
"route": "/admin/settings",
"requiredRoles": ["admin"], // Only admin role users see this page
"breadcrumbParent": "dashboard",
"isHome": false
}
// No role restriction — all app users see this page:
{
"pageId": "leads-list",
"title": "Leads",
"route": "/leads",
"requiredRoles": [] // Empty = visible to all app users
}
How Page Visibility Enforcement Works
| Scenario | Behavior |
|---|---|
| User has required role | Page appears in sidebar; navigating to the route renders the page normally |
| User lacks required role — sidebar | The sidebar item for this page is not rendered (not hidden, not disabled — completely absent) |
| User lacks required role — direct URL | Navigating to the URL directly returns a 403 page — the AppPage is never loaded |
| No requiredRoles set | All authenticated app users can access the page |
Role Match Semantics
Same as app-level access: a user needs at least one of the listed roles (OR logic). For AND logic, use a visibilityExpression on the page:
// Page visible only to users who are BOTH admin AND regional-manager
// (not possible with requiredRoles alone — use visibilityExpression instead)
{
"pageId": "regional-admin",
"title": "Regional Admin",
"route": "/regional-admin",
"requiredRoles": [], // Leave empty...
"visibilityExpression": "{{ context.roles.includes('admin') && context.roles.includes('regional-manager') }}"
}
Page Visibility vs. Sidebar Menu Items
Page visibility (defined on AppPage) is independent of menu item visibility (defined on NavItem). Both need to be consistent — if a user has a role that shows a menu item but not access to the page, clicking the menu item results in a 403.
Best practice: set requiredRoles on the AppPage and also set visibilityExpression on the corresponding NavItem with the same condition. The AppPage role check is the authoritative security gate; the NavItem visibility is just UX polish.
// AppPage
{ "pageId": "reports", "requiredRoles": ["manager", "admin"] }
// Matching NavItem — hides the menu item for non-managers automatically
{
"type": "item",
"label": "Reports",
"targetPageId": "reports",
"visibilityExpression": "{{ context.roles.includes('manager') || context.roles.includes('admin') }}"
}