Admin menus often arrive from the server: different roles see different entry points, and new pages do not require a frontend release. The work is not to render whatever arrives, but to hold the menu, the routes and the actions to one set of rules.
The menu contract
The menu arrives as groups plus items. Each item carries a path, a label or label key, an icon, its group, an order, a hidden flag, permission codes and children. Groups decide sidebar order, and nesting stops at four levels so a pathological payload cannot blow up the sidebar.
External items declare their own address and target: new window, same window or embedded frame. Embedding only works for sites that allow it — a site sending X-Frame-Options: DENY will render blank.
Tolerance also belongs on the frontend: odd paths, unknown icons and over-deep children are normalised, and every adjustment leaves a readable warning list. Read that list when wiring a real API instead of waiting for a user to report an error.
Routes only know registered pages
Dynamic routes pull their components from a local registry: the API can reference a registered page but cannot introduce new code. A missing session or permission lands on the sign-in page or a 403, never a blank screen.
Three layers, one rule set
- Menu: items are filtered by permission code, so unavailable entry points are not rendered.
- Routes: typing the URL directly hits the same guard and lands on 403.
- Actions:
<Can permission="customer:create">covers operations such as create and export, showing a disabled state instead of failing silently.
Permission codes support * for everything, module:* for a whole module and exact matches, so role differences fit in one list. The documentation covers the full contract, and the live demo lets two test accounts compare: at the same address the administrator sees the audit log while the test account gets a 403.