Alkademi Logo
Guides

Plugin Creation Guide

This guide creates a simple welcome-banner plugin that adds a dashboard page and a new sidebar item.

Plugin Creation Guide

This guide creates a simple welcome-banner plugin that adds a dashboard page and a new sidebar item.

Step 1: Create Folder Structure

mkdir -p plugins/welcome-banner/pages/dashboard/welcome-banner
mkdir -p plugins/welcome-banner/components

Step 2: Create Layer Configuration

Create plugins/welcome-banner/nuxt.config.ts:

export default defineNuxtConfig({
  imports: {
    dirs: ['stores']
  }
})

imports.dirs is optional. Use it when the plugin has a stores/ folder or other auto-import folders.

Step 3: Create Plugin Configuration

Create plugins/welcome-banner/plugin.config.ts:

import type { PluginConfig } from '../../shared/types/plugin'

export default {
  enabled: true,
  routes: [
    {
      key: 'welcome-banner',
      path: '/dashboard/welcome-banner',
      icon: 'i-heroicons-megaphone',
      label: 'Welcome Banner'
    }
  ]
} satisfies PluginConfig

The routes field is the source for static sidebar menu items. The path should match the page you create.

Step 4: Create Plugin Page

Create plugins/welcome-banner/pages/dashboard/welcome-banner/index.vue:

<template>
  <main class="space-y-4">
    <WelcomeBanner />
  </main>
</template>

Step 5: Create Component

Create plugins/welcome-banner/components/WelcomeBanner.vue:

<template>
  <section class="rounded-lg border border-primary-200 bg-primary-50 p-6">
    <h1 class="text-xl font-semibold text-primary-950">
      Welcome to the LMS
    </h1>
    <p class="mt-2 text-primary-700">
      This page comes from a plugin loaded as a Nuxt Layer.
    </p>
  </section>
</template>

Step 6: Add Dynamic Sidebar (Optional)

If the plugin needs to add child menu items from an API, create plugins/welcome-banner/sidebar.ts:

import { defineSidebarProvider } from '../../shared/types/menu'

export default defineSidebarProvider(({ route }) => {
  if (!route.path.startsWith('/dashboard/welcome-banner')) {
    return []
  }

  return [
    {
      key: 'welcome-banner',
      path: '/dashboard/welcome-banner',
      icon: 'i-heroicons-megaphone',
      label: 'Welcome Banner',
      source: 'plugin',
      pluginId: 'welcome-banner',
      children: [
        {
          key: 'welcome-banner.settings',
          path: '/dashboard/welcome-banner/settings',
          icon: 'i-heroicons-cog-6-tooth',
          label: 'Settings',
          source: 'plugin',
          pluginId: 'welcome-banner'
        }
      ]
    }
  ]
})

Because the key matches the static route item, this item will be merged and the child menu will appear under Welcome Banner.

Replacing a Core

If this plugin should replace a core, add replaces:

export default {
  enabled: true,
  replaces: ['program'],
  routes: [
    {
      key: 'welcome-banner',
      path: '/dashboard/welcome-banner',
      icon: 'i-heroicons-megaphone',
      label: 'Welcome Banner'
    }
  ]
}

When active, the program core will not be loaded by auto-discovery.

Verification

Run:

pnpm dev

Open /dashboard/welcome-banner. If enabled: true, the plugin will be loaded as a layer and the sidebar item will appear from plugin.config.ts.

Copyright © 2026