Availability
| Edition | Deployment Type |
|---|---|
| Community & Enterprise | Self-Managed, Hybrid |
Overview
Portal UI plugins enable you to:- Add Portal Pages: Register new routes in the portal navigation
- Extend Portal Sidebar: Add sections and links to the portal drawer
- Serve WebComponents: Use any frontend framework (same asset serving as admin UI)
- Handle Portal RPC: Define backend endpoints that receive authenticated user context
- Control Visibility: Restrict portal pages to specific user groups
- Combine with Admin UI: A single plugin can have both admin and portal interfaces
Use Cases
- Support ticket systems for end users
- Custom resource browsers or dashboards
- User feedback and survey forms
- Forum or community features
- Self-service configuration pages
- Data submission and approval workflows
Architecture
Portal UI plugins use a separate security scope from admin UI plugins. This is enforced at every level:| Aspect | Admin UI | Portal UI |
|---|---|---|
| API prefix | /api/v1/plugins/ | /common/plugins/ |
| Auth required | Admin only | Any authenticated user |
| RPC handler | HandleRPC() | HandlePortalRPC() |
| User context | None (admin implied) | PortalUserContext with user ID, email, groups |
| gRPC method | Call | PortalCall |
| Hook type | studio_ui | portal_ui |
| Visibility | All admins | Filterable by user group |
Quick Start
1. Project Structure
2. Create Manifest
The manifest declares both admin (ui) and portal (portal) UI sections. The portal section uses PortalUISlot which includes a groups field for visibility filtering.
Expandable
Group-Based Visibility
Thegroups field on portal slots controls which users can see the page:
groups value | Visibility |
|---|---|
[] (empty array) | All portal users |
["engineering", "support"] | Users in at least one of these groups |
["admin-team"] | Only users in the “admin-team” group |
3. Implement the Plugin
A portal UI plugin must implement bothUIProvider (for assets and manifest) and PortalUIProvider (for portal RPC):
Expandable
4. Create Portal WebComponent
Portal WebComponents receive aportalPluginAPI object (injected by the portal plugin loader) for making RPC calls:
Expandable
- Admin components receive
this.pluginAPI(routes toHandleRPC) - Portal components receive
this.portalPluginAPI(routes toHandlePortalRPC)
5. Create Admin WebComponent (Optional)
If your plugin also has an admin interface, create a separate WebComponent that usesthis.pluginAPI:
Expandable
PortalUserContext
Every portal RPC call includes aPortalUserContext with the authenticated user’s information:
- Authorization: Check if the user has permission for the requested operation
- Data scoping: Return only data belonging to the calling user
- Audit logging: Record who performed each action
- Group-based features: Enable features based on group membership
Expandable
Manifest Reference
Portal Slots
Theportal section in the manifest is parallel to the ui section:
Expandable
| Field | Type | Required | Description |
|---|---|---|---|
slot | string | Yes | Slot identifier. Currently supports portal_sidebar.section |
label | string | Yes | Display label in the sidebar |
icon | string | No | Icon name for the sidebar entry |
groups | string[] | No | Allowed user groups. Empty = all portal users |
items | array | Yes | Routes/components to register |
items[].type | string | Yes | Must be "route" |
items[].path | string | Yes | Route path (should start with /portal/plugins/) |
items[].title | string | Yes | Page title |
items[].mount.kind | string | Yes | Mount type: "webc" or "iframe" |
items[].mount.tag | string | Yes | Custom element tag name |
items[].mount.entry | string | Yes | JavaScript entry point path |
Capabilities and Permissions
Portal UI plugins must declare both hook types and permissions:studio_uiin hooks enables admin UI + asset servingportal_uiin hooks enables portal RPC handling- Both are required for a plugin with portal UI (assets are served via
UIProvider)
Route Path Convention
Portal plugin routes should follow the pattern/portal/plugins/{plugin-name}/{page} to avoid conflicts with built-in portal routes:
API Endpoints
Portal plugin API endpoints are under/common/ (authenticated, no admin required):
| Endpoint | Method | Description |
|---|---|---|
/common/plugins/portal-ui-registry | GET | Get portal UI components (filtered by user groups) |
/common/plugins/portal-sidebar-menu | GET | Get portal sidebar items (filtered by user groups) |
/common/plugins/:id/portal-rpc/:method | POST | Call portal RPC method on plugin |
/common/plugins/assets/:id/*filepath | GET | Serve plugin static assets |
Portal RPC Call Flow
Example: Portal Feedback Plugin
A complete working example is available atexamples/plugins/studio/portal-feedback/.
This plugin demonstrates:
- Portal form for users to submit feedback (
HandlePortalRPC) - Admin dashboard to view all submissions (
HandleRPC) - Shared asset serving between admin and portal UIs
- Group-based visibility (set to
[]for all users) waitForAPIAndLoad()pattern for WebComponent API injection timing
Best Practices
Security
- Never trust portal input - Always validate and sanitize data in
HandlePortalRPC - Scope data to users - Use
userCtx.UserIDto ensure users only see their own data - Keep admin operations in HandleRPC - Destructive operations (delete, admin overrides) should stay in the admin-only
HandleRPCmethod - Check groups in RPC handlers - Even though sidebar visibility is filtered, users could call the RPC endpoint directly. Validate group membership in
HandlePortalRPCfor sensitive operations
Performance
- Use KV storage for persistence - In-memory data is lost on plugin restart
- Cache frequently accessed data - Use
sync.Mapor similar for hot data - Keep portal pages lightweight - Portal users expect fast page loads
WebComponent Patterns
- Wait for API injection - Use the
waitForAPIAndLoad()pattern instead of calling the API inconnectedCallback()directly - Handle errors gracefully - Show user-friendly messages, not stack traces
- Use Shadow DOM - Prevents style conflicts with the host application
- Escape user content - Always escape HTML in dynamic content to prevent XSS