@mostajs/menu
v1.0.2
Published
Configurable dashboard sidebar with permission-based menu filtering
Downloads
276
Maintainers
Readme
@mostajs/menu
Configurable dashboard sidebar with permission-based menu filtering. Provides a collapsible sidebar driven by a declarative MenuConfig object, with built-in RBAC support.
Installation
npm install @mostajs/menu lucide-reactQuick Start
import { DashboardSidebar } from '@mostajs/menu'
import type { MenuConfig } from '@mostajs/menu'
import {
LayoutDashboard, Users, Ticket, ScanLine,
Settings, BarChart3, Shield, UserCog,
} from 'lucide-react'
// 1. Define your menu config
const menuConfig: MenuConfig = {
// Top-level direct links
items: [
{ label: 'Dashboard', href: '/dashboard', icon: LayoutDashboard },
{ label: 'Clients', href: '/dashboard/clients', icon: Users, permission: 'client:view' },
{ label: 'Tickets', href: '/dashboard/tickets', icon: Ticket, permission: 'ticket:view' },
{ label: 'Scanner', href: '/dashboard/scan', icon: ScanLine, permission: 'scan:validate' },
],
// Collapsible groups
groups: [
{
label: 'Administration',
icon: Settings,
items: [
{ label: 'Users', href: '/dashboard/users', icon: UserCog, permission: 'user:manage' },
{ label: 'Roles', href: '/dashboard/roles', icon: Shield, permission: 'role:manage' },
{ label: 'Statistics', href: '/dashboard/stats', icon: BarChart3, permission: 'stats:view' },
],
},
],
}
// 2. Use in your layout
export default function DashboardLayout({ children }) {
return (
<div style={{ display: 'flex' }}>
<DashboardSidebar
config={menuConfig}
user={{
name: 'Dr Hamid',
role: 'admin',
permissions: ['client:view', 'ticket:view', 'scan:validate', 'user:manage'],
}}
appName="SecuAccessPro"
appIcon={Shield}
t={(key) => translations[key] || key}
hasPermission={(perms, required) => perms.includes(required)}
onLogout={() => signOut()}
/>
<main style={{ flex: 1 }}>{children}</main>
</div>
)
}Component
<DashboardSidebar />
A full-height sidebar with header (app icon + name), collapsible navigation groups, and a user footer with logout.
Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| config | MenuConfig | required | Menu structure (items + groups) |
| user | SidebarUser | required | Current user info |
| appName | string | 'App' | App name in sidebar header |
| appIcon | ElementType | Shield | Icon component for sidebar header |
| t | (key: string) => string | identity | Translation function for labels |
| hasPermission | (permissions: string[], required: string) => boolean | () => true | Permission check function |
| onLogout | () => void | — | Logout handler (shows logout button when provided) |
Types
MenuConfig
interface MenuConfig {
/** Top-level direct links (always visible, no collapsing) */
items: MenuItem[]
/** Collapsible groups with sub-items */
groups: MenuGroup[]
}MenuItem
interface MenuItem {
label: string // Display label (or i18n key if using t())
href: string // Route path
icon: ElementType // Lucide icon or any React component
permission?: string // Required permission (hidden if user lacks it)
}MenuGroup
interface MenuGroup {
label: string // Group header label
icon: ElementType // Group header icon
items: MenuItem[] // Group children
}SidebarUser
interface SidebarUser {
name?: string | null // Displayed in footer
role?: string // Displayed under name
permissions?: string[] // Used by hasPermission()
}Features
Permission-based filtering
Menu items with a permission property are only visible when hasPermission(user.permissions, item.permission) returns true. Groups with no visible children are automatically hidden.
// Only users with 'admin:access' see this item
{ label: 'Admin Panel', href: '/admin', icon: Shield, permission: 'admin:access' }Collapsible sidebar
Click the chevron in the header to collapse the sidebar to icon-only mode (64px). In collapsed mode, labels are hidden and groups show only their icon.
Auto-expand active group
Groups containing the currently active route are automatically expanded on mount.
Internationalization
Pass a t() function to translate menu labels and user role:
<DashboardSidebar
config={menuConfig}
user={user}
t={(key) => {
// Menu labels are passed as-is
// User role uses format: auth.roles.{role}
// Logout button uses: auth.logout.button
return i18n.t(key)
}}
/>Translation keys used:
- Each
MenuItem.labelis passed throught() auth.roles.{user.role}for the role displayauth.logout.buttonfor the logout button title
Styling
The sidebar uses Tailwind CSS classes. It requires:
- A Tailwind-configured project
- The
skyandgraycolor palette
The sidebar takes full viewport height (h-screen) and is positioned as a flex child. Wrap it in a flex container with your main content:
<div className="flex">
<DashboardSidebar ... />
<main className="flex-1 overflow-y-auto">{children}</main>
</div>License
MIT
