Menu Configuration
The Admin UI menu can be configured to organize entities into groups and add custom navigation items.
Overview
By default, the menu is auto-generated with all entities. You can customize it to:
- Group entities into categories
- Add custom navigation links
- Add custom action menu items
- Control the order of items
Configuration
menu?: {
[groupName: string]: (string | MenuItemConfig)[]
}| Type | Default | Description |
|---|---|---|
object | auto-generated | Menu structure with groups |
Basic Example
{
menu: {
'Users': ['User', 'Role', 'Permission'],
'Content': ['Article', 'Category', 'Tag'],
'E-Commerce': ['Product', 'Order', 'Customer']
}
}Menu Items
Entity Reference
Reference an entity by name:
{
menu: {
'Content': ['Article', 'Category']
}
}Custom Link
Add a navigation link:
{
menu: {
'Content': [
'Article',
{
label: 'Media Library',
path: '/admin/media'
}
]
}
}External Link
Open in new window:
{
menu: {
'Help': [
{
label: 'Documentation',
path: 'https://docs.example.com',
newWindow: true
}
]
}
}Custom Action
Execute custom logic:
{
menu: {
'Actions': [
{
label: 'Clear Cache',
confirm: 'Clear all cached data?',
action: async ({ principal, runtime }) => {
await clearCache();
return { message: 'Cache cleared successfully' };
}
}
]
}
}Menu Item Configuration
type MenuItemConfig = {
label: string;
path?: string;
newWindow?: boolean;
confirm?: string;
action?: (params: ActionParams) => Promise<ActionResult>;
}
type ActionParams = {
principal: Principal;
runtime: ActiveQLServer;
activeAdmin: ActiveAdmin;
}
type ActionResult = {
message?: string;
redirect?: string;
}Action Results
Show Message
{
action: async () => {
await doSomething();
return { message: 'Operation completed' };
}
}Redirect
{
action: async () => {
const report = await generateReport();
return { redirect: `/download/${report.id}` };
}
}Complete Example
{
menu: {
'Main': [
'User',
'Product',
'Order'
],
'Content': [
'Article',
'Category',
'Tag',
{
label: 'Media Library',
path: '/admin/media'
}
],
'Settings': [
'SystemSetting',
'EmailTemplate',
{
label: 'Edit Configuration',
path: '/admin/config'
}
],
'Reports': [
{
label: 'Sales Report',
action: async ({ runtime }) => {
const report = await generateSalesReport(runtime);
return { redirect: `/download/report/${report.id}` };
}
},
{
label: 'User Activity',
action: async ({ runtime }) => {
const report = await generateActivityReport(runtime);
return { redirect: `/download/report/${report.id}` };
}
}
],
'Actions': [
{
label: 'Clear Cache',
confirm: 'This will clear all cached data. Continue?',
action: async () => {
await clearAllCache();
return { message: 'Cache cleared successfully' };
}
},
{
label: 'Rebuild Search Index',
confirm: 'Rebuild the search index? This may take a few minutes.',
action: async ({ runtime }) => {
await rebuildSearchIndex(runtime);
return { message: 'Search index rebuilt' };
}
},
{
label: 'Send Newsletter',
confirm: 'Send newsletter to all subscribers?',
action: async ({ runtime }) => {
const count = await sendNewsletter(runtime);
return { message: `Newsletter sent to ${count} subscribers` };
}
}
],
'Help': [
{
label: 'Documentation',
path: 'https://docs.example.com',
newWindow: true
},
{
label: 'API Reference',
path: 'https://api.example.com/docs',
newWindow: true
},
{
label: 'Support',
path: 'mailto:support@example.com'
}
]
}
}Dynamic Menu
You can generate menu items dynamically:
{
menu: {
'Content': await getContentEntities(),
'Dynamic': generateDynamicMenuItems()
}
}
function generateDynamicMenuItems() {
const items = ['Article', 'Page'];
if (process.env.FEATURE_BLOG === 'true') {
items.push('BlogPost');
}
return items;
}Hiding Entities from Menu
Entities not included in the menu configuration are hidden from navigation but still accessible via URL:
{
menu: {
'Main': ['User', 'Product']
// Order and Customer are not in menu but still accessible
}
}To completely hide an entity, you would need to configure permissions.
Built-in Menus
ActiveQL Admin UI provides several built-in menu groups that can be customized or disabled:
Developer Menu
The Developer menu contains useful development tools:
{
menu: {
Developer: {
seed: {
label: 'Seed data',
confirm: 'Reset and seed database?',
action: async ({ activeAdmin, principal }) => {
await activeAdmin.runtime.seed(true);
return { message: 'Database seeded successfully' };
}
},
graphql: {
label: 'GraphQL Studio',
path: '/graphql',
newWindow: true
},
swagger: {
label: 'Swagger / REST API',
path: '/docs',
newWindow: true
},
plantuml: {
label: 'Entity Diagram',
path: '/active-admin/plantuml',
newWindow: true
},
repository: {
label: 'Repository',
// Auto-detected from package.json
newWindow: true
},
configuration: {
label: 'Show Configuration',
path: '/active-admin/configuration',
newWindow: true
}
}
}
}Disable specific items by setting them to false:
{
menu: {
Developer: {
seed: false, // Hide seed action
swagger: false // Hide swagger link
}
}
}QueryMutation Menu
Automatically generates menu items for all GraphQL queries and mutations without arguments:
{
menu: {
QueryMutation: {
// Auto-populated with:
// - All queries with no arguments
// - All mutations with no arguments (with confirmation)
// Organized with dividers between groups
}
}
}This menu is dynamically generated from your GraphQL schema. Example generated items:
ping- Query--- Queries ---(divider)systemStatus- Query--- Mutations ---(divider)resetCache- Mutation (with confirmation)rebuildIndex- Mutation (with confirmation)
Disable QueryMutation menu:
{
menu: {
QueryMutation: false
}
}Entities Menu
The Entities menu is auto-generated based on your domain entities. By default, it lists all entities:
{
menu: {
Entities: ['User', 'Product', 'Order', 'Category']
}
}Dynamic generation based on realms:
{
menu: {
Entities: async (activeAdmin) => {
const entities = activeAdmin.runtime.entities;
// Group by realm or custom logic
return {
'Core': ['User', 'Role'],
'Commerce': ['Product', 'Order'],
'Content': ['Article', 'Page']
};
}
}
}Organize by realm (if entities have realm configuration):
// Entities are automatically grouped by their realm property
{
menu: {
Entities: (activeAdmin) => {
const grouped = {};
const entities = activeAdmin.runtime.entities;
Object.keys(entities).forEach(name => {
const realm = entities[name].realm || 'Default';
if (!grouped[realm]) grouped[realm] = [];
grouped[realm].push(name);
});
return grouped;
}
}
}Custom Extra Menus
Add custom menu groups with the Extra property:
{
menu: {
Extra: {
'Analytics': [
{
label: 'User Analytics',
action: async ({ runtime }) => {
const stats = await runtime.entity('User').stats();
return {
message: `Total users: ${stats.count}`
};
}
},
{
label: 'Sales Dashboard',
path: '/analytics/sales',
newWindow: true
}
],
'Tools': [
{
label: 'Export Data',
action: async ({ runtime }) => {
const file = await exportAllData(runtime);
return { redirect: `/download/${file}` };
}
}
]
}
}
}Complete Example with Built-in Menus
export const activeAdminConfig: ActiveAdminConfig = {
title: 'My Admin UI',
menu: {
// Customize Developer menu
Developer: {
seed: {
label: 'Reset & Seed Database',
confirm: 'This will delete all data and reset the database. Continue?',
action: async ({ activeAdmin, principal }) => {
// Custom seed logic
if (!activeAdmin.runtime.principalHasRole('admin', { principal })) {
return { message: 'Only admins can seed data' };
}
await activeAdmin.runtime.seed(true);
return { message: 'Database reset and seeded' };
}
},
graphql: true,
swagger: true,
plantuml: false, // Disable diagram
repository: true,
configuration: true
},
// Keep auto-generated QueryMutation menu
QueryMutation: true,
// Customize Entities menu
Entities: {
'User Management': ['User', 'Role', 'Permission'],
'E-Commerce': ['Product', 'Order', 'Customer', 'Payment'],
'Content': ['Article', 'Category', 'Tag', 'Media']
},
// Add custom menus
Extra: {
'Reports': [
{
label: 'Monthly Sales',
action: async ({ runtime }) => {
const report = await generateMonthlySales(runtime);
return { redirect: `/reports/${report.id}` };
}
}
]
}
}
};Menu Configuration Type
type ActiveAdminMenuConfig = {
Developer?: DeveloperMenu | false;
QueryMutation?: MenuType | MenuTypeFn | false;
Entities?: EntityMenu | EntityMenuFn | false;
Extra?: {
[groupName: string]: MenuType | MenuTypeFn;
};
};
type DeveloperMenu = {
seed?: MenuAction | false;
graphql?: MenuAction | false;
swagger?: MenuAction | false;
plantuml?: MenuAction | false;
repository?: MenuAction | false;
configuration?: MenuAction | false;
};
type MenuAction = {
label?: string;
path?: string;
newWindow?: boolean;
confirm?: boolean | string;
action?: (params: MenuActionParams) => Promise<MenuActionResult>;
};Menu Icons
The Admin UI uses Tabler icons. You can reference them in custom templates if you create custom menu partials.