Menu
The sidebar is fully config-driven: you describe it as a typed MenuNode[] and pass it to the layout's menuItems input. No template markup, no manual active-state plumbing.
The MenuNode model
MenuNode is a discriminated union of three node types, keyed on type:
| Type | Renders as | Key fields |
|---|---|---|
'header' | Non-interactive section label | text |
'item' | Clickable link (leaf) | text, route or href, icon, badge, visible |
'group' | Collapsible submenu (treeview) | text, icon, children, visible |
Fields
text— the label (all node types).route— an Angular route, rendered withRouterLink(items only).href— an external URL, rendered as a plain anchor; pair withtarget: '_blank'(items only).icon— a Bootstrap Icons class, with or without thebiprefix (e.g.'bi-speedometer'); items and groups.iconColor— aBootstrapThemefor the icon color.badge/badgeColor— an optional pill (string | number) with aBootstrapThemecolor.children— the nestedMenuNode[](groups only).visible— whenfalse, the node is not rendered. Wire it to your auth/role logic to hide items without removing them from the config.
A real menu
import type { MenuNode } from '@adminlte/angular';
export const MENU: MenuNode[] = [
{ type: 'header', text: 'MAIN NAVIGATION' },
{
type: 'group',
text: 'Dashboard',
icon: 'bi-speedometer',
children: [
{ type: 'item', text: 'Dashboard v1', route: '/', icon: 'bi-circle' },
{ type: 'item', text: 'Dashboard v2', route: '/dashboard/v2', icon: 'bi-circle' },
{ type: 'item', text: 'Dashboard v3', route: '/dashboard/v3', icon: 'bi-circle' },
],
},
{
type: 'group',
text: 'UI Elements',
icon: 'bi-tree-fill',
children: [
{ type: 'item', text: 'General', route: '/ui/general', icon: 'bi-circle' },
{ type: 'item', text: 'Icons', route: '/ui/icons', icon: 'bi-circle' },
{ type: 'item', text: 'Timeline', route: '/ui/timeline', icon: 'bi-circle' },
],
},
{ type: 'item', text: 'Components', route: '/components', icon: 'bi-puzzle', badge: 'New', badgeColor: 'info' },
{ type: 'header', text: 'EXAMPLES' },
{ type: 'item', text: 'Profile', route: '/profile', icon: 'bi-person-badge' },
{ type: 'item', text: 'Calendar', route: '/calendar', icon: 'bi-calendar3' },
{ type: 'item', text: 'Admin only', route: '/admin', icon: 'bi-lock', visible: false },
{ type: 'item', text: 'AdminLTE.io', href: 'https://adminlte.io', icon: 'bi-globe', target: '_blank' },
];
Active-link detection
The sidebar highlights the active item — and auto-opens its parent group — by comparing each item's route against the currentPath input on <lte-dashboard-layout>. Feed that input from the Angular Router so it tracks navigation:
readonly currentPath = toSignal(
this.router.events.pipe(
filter((e): e is NavigationEnd => e instanceof NavigationEnd),
map((e) => e.urlAfterRedirects.split('?')[0]),
startWith(this.router.url.split('?')[0]),
),
{ initialValue: '/' },
);
Pass [accordion]="true" to keep only one group open at a time.
Command palette source
The same menu config powers the ⌘K command palette: the library flattens it into searchable commands (the flattenMenuToCommands utility is also exported if you need it directly). Open the palette with ⌘K / Ctrl+K or the topbar search icon; selecting a result navigates via the Router.
Because the menu is plain typed data, you can build it dynamically — filtering by role, injecting badges from a signal, or composing it from feature modules — and the sidebar, active detection and command palette all stay in sync.