Plugin System
Plugin System
A plugin is an additional Nuxt Layer placed under plugins/. Plugins are used to add new features, replace selected cores, or enrich navigation without changing source files under cores/.
Plugin Structure
plugins/my-plugin/
|-- components/ # Plugin components
|-- pages/ # Plugin pages
|-- server/api/ # Plugin Nitro endpoints, when needed
|-- stores/ # Plugin Pinia stores, when needed
|-- plugin.config.ts # Plugin configuration
|-- sidebar.ts # Optional: dynamic sidebar menu provider
`-- nuxt.config.ts # Marks the plugin as a Nuxt Layer
The required files for auto-discovery are nuxt.config.ts and plugin.config.ts.
Current Plugin Configuration
The plugin contract follows PluginConfig in shared/types/plugin.ts. The main fields currently used are:
enabled: determines whether the plugin is active.routes: route definitions that also become static sidebar menu items.replaces: optional list of core ids to replace.
Example:
import type { PluginConfig } from '../../shared/types/plugin'
export default {
enabled: true,
replaces: ['program'],
routes: [
{
key: 'custom-class',
path: '/dashboard/custom-class',
icon: 'i-heroicons-sparkles',
label: 'Custom Class'
}
]
} satisfies PluginConfig
The current project does not use name, displayName, version, overrideMode, or targetCore as the primary plugin contract.
Routes and Sidebar
Each item in routes must have:
path: the dashboard URL.icon: a Nuxt UI/Iconify icon name, for examplei-heroicons-sparkles.label: the menu label.
Supported optional fields:
key: a stable id for menu merging.children: child menu items.permission: a single permission or an array of permissions.meta: custom metadata.
routes is used by useSidebarMenu() to create static menu items. If a plugin needs dynamic menu items, add sidebar.ts.
Dynamic Sidebar
A plugin can provide sidebar.ts that exports a sidebar provider:
import { defineSidebarProvider } from '../../shared/types/menu'
export default defineSidebarProvider(async ({ route }) => {
if (!route.path.startsWith('/dashboard/custom-class')) {
return []
}
const classes = await $fetch('/api/custom-class/sidebar')
return [
{
key: 'custom-class',
path: '/dashboard/custom-class',
icon: 'i-heroicons-sparkles',
label: 'Custom Class',
source: 'plugin',
pluginId: 'custom-class',
children: classes.map((item) => ({
key: `custom-class.${item.id}`,
path: `/dashboard/custom-class/${item.id}`,
icon: 'i-heroicons-document-text',
label: item.label,
source: 'plugin',
pluginId: 'custom-class'
}))
}
]
})
Menu items from sidebar.ts are merged with static menu items by key or path.
Learn about replacement behavior in Override System.
Architecture: Nuxt Layers
Alkademi LMS Open Source is built as a modular monolith. Every feature still runs inside one Nuxt application, but each domain feature is isolated as a Nuxt Layer so it can be maintained, replaced, or extended more easily.
Override System
The current implementation uses two main mechanisms: extend through plugin routes/layers, and replace core through the replaces field. There is no overrideMode field read by the current source.
