BrainDrive UI page registration patterns - use when adding pages, routes, or modules to plugins
UI page registration is how your plugin tells BrainDrive where to display your component. Without registration, BrainDrive doesn't know where to mount your React component.
Each UI page needs:
dashboard, settings)./Dashboard)DashboardComponentDefine pages in the modules metadata:
# lifecycle_manager.py
self.modules = [
{
"module_id": "main",
"name": "Main Dashboard",
"role": "frontend",
"description": "Main plugin UI",
"pages": [
{
"slug": "dashboard",
"title": "Dashboard",
"route": "/plugin/my-plugin/dashboard",
"icon": "BarChart3",
"component": "DashboardPage",
"description": "Main dashboard view"
},
{
"slug": "settings",
"title": "Settings",
"route": "/plugin/my-plugin/settings",
"icon": "Settings",
"component": "SettingsPage",
"description": "Plugin configuration"
}
],
"component": {
"bundlelocation": "dist/remoteEntry.js",
"scope": "MyPlugin",
"module": "./index",
"container": "MY_PLUGIN_CONTAINER"
}
}
]
Export metadata from your component:
// src/MyPlugin.tsx
export const MyPlugin: React.FC = () => {
return <div>Main plugin UI</div>;
};
// Register pages
MyPlugin.pages = [
{
slug: 'dashboard',
title: 'Dashboard',
component: 'DashboardPage',
route: '/plugin/my-plugin/dashboard'
},
{
slug: 'settings',
title: 'Settings',
component: 'SettingsPage',
route: '/plugin/my-plugin/settings'
}
];
# lifecycle_manager.py
self.modules = [
{
"module_id": "main",
"name": "Complete Plugin",
"role": "frontend",
"description": "Full-featured plugin",
"pages": [
{
"slug": "home",
"title": "Home",
"route": "/plugin/task-manager/home",
"icon": "Home",
"component": "HomePage",
"description": "Main home page",
"default": True, # Show this page first
"public": False # Requires authentication
},
{
"slug": "tasks",
"title": "My Tasks",
"route": "/plugin/task-manager/tasks",
"icon": "CheckCircle",
"component": "TasksPage",
"description": "View and manage tasks",
"default": False,
"public": False
},
{
"slug": "analytics",
"title": "Analytics",
"route": "/plugin/task-manager/analytics",
"icon": "BarChart3",
"component": "AnalyticsPage",
"description": "Task completion analytics",
"default": False,
"public": False
},
{
"slug": "settings",
"title": "Settings",
"route": "/plugin/task-manager/settings",
"icon": "Settings",
"component": "SettingsPage",
"description": "Plugin configuration",
"default": False,
"public": False
}
],
"component": {
"bundlelocation": "dist/remoteEntry.js",
"scope": "TaskManager",
"module": "./index",
"container": "TASK_MANAGER_CONTAINER"
}
}
]
Your component file must export each registered component:
// src/index.tsx
export const HomePage = lazy(() => import('./pages/HomePage'));
export const TasksPage = lazy(() => import('./pages/TasksPage'));
export const AnalyticsPage = lazy(() => import('./pages/AnalyticsPage'));
export const SettingsPage = lazy(() => import('./pages/SettingsPage'));
// Entry point for Module Federation
export default () => HomePage;
// Or use a router
export const TaskManager = () => {
const [currentPage] = useSearchParams();
switch(currentPage.get('slug')) {
case 'tasks': return <TasksPage />;
case 'analytics': return <AnalyticsPage />;
case 'settings': return <SettingsPage />;
default: return <HomePage />;
}
};
export default TaskManager;
| Field | Type | Required | Example |
|---|---|---|---|
slug | string | ✅ | "dashboard" |
title | string | ✅ | "Dashboard" |
route | string | ✅ | "/plugin/my-plugin/dashboard" |
icon | string | ✅ | "BarChart3" |
component | string | ✅ | "DashboardPage" |
description | string | ❌ | "Main dashboard view" |
default | boolean | ❌ | true (show first) |
public | boolean | ❌ | false (requires auth) |
children | array | ❌ | Sub-pages/nested routes |
Routes should follow this pattern:
/plugin/{plugin-slug}/{page-slug}
Examples:
/plugin/task-manager/home/plugin/task-manager/tasks/plugin/task-manager/settings/plugin/data-dashboard/analytics/plugin/data-dashboard/exportChoose from BrainDrive's icon library (Feather icons):
# Navigation
"Home", "Menu", "ChevronRight", "ChevronDown"
# Content
"FileText", "Image", "MessageSquare", "Bell"
# Actions
"Plus", "Edit", "Trash2", "Download", "Upload"
# Status
"CheckCircle", "AlertCircle", "Info", "HelpCircle"
# Data
"BarChart3", "LineChart", "Pie Chart", "TrendingUp"
# Organization
"Folder", "Archive", "Tag", "Filter"
# Settings
"Settings", "Sliders", "Tool", "Zap"
# Users
"Users", "User", "UserCheck", "UserX"
# Calendar
"Calendar", "Clock", "Clock3"
For hierarchical structures:
{
"slug": "settings",
"title": "Settings",
"route": "/plugin/my-plugin/settings",
"icon": "Settings",
"component": "SettingsLayout",
"children": [
{
"slug": "general",
"title": "General",
"route": "/plugin/my-plugin/settings/general",
"component": "GeneralSettings"
},
{
"slug": "notifications",
"title": "Notifications",
"route": "/plugin/my-plugin/settings/notifications",
"component": "NotificationSettings"
},
{
"slug": "integrations",
"title": "Integrations",
"route": "/plugin/my-plugin/settings/integrations",
"component": "IntegrationSettings"
}
]
}
Register plugin actions in context menus:
{
"module_id": "context-menu",
"name": "Context Actions",
"role": "plugin",
"actions": [
{
"slug": "add-to-plugin",
"title": "Add to My Plugin",
"context": ["conversation", "message"],
"handler": "addToPlugin"
},
{
"slug": "share-via-plugin",
"title": "Share via Plugin",
"context": ["conversation"],
"handler": "shareViaPlugin"
}
]
}
For plugin-generated pages:
// During plugin initialization
export const MyPlugin: React.FC = () => {
const [pages, setPages] = useState([
{ slug: 'default', title: 'Default', component: 'DefaultPage' }
]);
const addPage = (pageConfig) => {
setPages([...pages, pageConfig]);
// Notify BrainDrive of new page
window.dispatchEvent(new CustomEvent('braindrive:pages-updated', {
detail: pages
}));
};
return <div>Dynamic pages setup</div>;
};
slug is unique within modulecomponent is exported from your bundle/plugin/{slug}/{page-slug} patterndefault: truecomponent field matches exported component namechildren have component fields❌ Component name doesn't match export:
# In lifecycle_manager
"component": "DashboardPage"
# But exported as
export const Dashboard = ... // WRONG! Names don't match
✅ Fix: Match names exactly
export const DashboardPage = ... // CORRECT!
❌ Routes don't follow pattern:
"route": "/my-plugin/dashboard" # Missing /plugin/
✅ Fix: Use correct pattern
"route": "/plugin/my-plugin/dashboard"
❌ No default page:
"pages": [
{ "slug": "settings", ... },
{ "slug": "about", ... }
# BrainDrive doesn't know what to show!
]
✅ Fix: Set one as default
"pages": [
{ "slug": "dashboard", ..., "default": True },
{ "slug": "settings", ... },
{ "slug": "about", ... }
]
Golden Rule: Every component you register must be exported from your bundle, and every route must follow BrainDrive's naming convention.