# Admin Menu

The admin sidebar is config-driven. To make your package show up there, you write a small PHP config file listing the menu entries and merge it into the core menu.admin config from your service provider. This page picks up from Routes โ€” the named route you created (admin.examples.index) is what each menu entry will point at.

# ๐Ÿ“ Create the menu config file

Inside your package, add a Config/ folder with menu.php:

packages
โ””โ”€โ”€ Webkul
    โ””โ”€โ”€ Example
        โ””โ”€โ”€ src
            โ”œโ”€โ”€ ...
            โ””โ”€โ”€ Config
                โ””โ”€โ”€ menu.php

Each entry in the array describes one menu item:

Key Required Purpose
key yes Unique identifier for the item. Use dot-notation to nest (examples.list).
name yes Translation key for the visible label.
route yes Named Laravel route this item links to.
sort no Display order (lower = earlier). Defaults to position in the file.
icon no CSS class for the icon to show next to the label.

# Example: top-level item

<?php

return [
    [
        'key' => 'examples',
        'name' => 'example::app.examples.menu.title',
        'route' => 'admin.examples.index',
        'sort' => 2,
        'icon' => 'icon-example',
    ],
];

# ๐ŸŽจ Add a menu icon

Krayin's icons are sprite-driven โ€” you give the class an absolute position into a shared SVG sprite. Add the CSS rule to your package's stylesheet:

packages
โ””โ”€โ”€ Webkul
    โ””โ”€โ”€ Example
        โ””โ”€โ”€ src
            โ””โ”€โ”€ Resources
                โ””โ”€โ”€ assets
                    โ”œโ”€โ”€ css
                    โ”‚   โ””โ”€โ”€ app.css
                    โ”œโ”€โ”€ js
                    โ”‚   โ””โ”€โ”€ app.js
                    โ””โ”€โ”€ images
.icon-example {
    background-position: -371px -2px;
    background-image: url("../images/sprite-main.svg");
}

See Assets for bundling the CSS into the admin build.

# โš™๏ธ Merge the config into the service provider

mergeConfigFrom() blends your menu.php into the global menu.admin config so Krayin's sidebar picks it up. Add the call inside register():

<?php

namespace Webkul\Example\Providers;

use Illuminate\Support\ServiceProvider;

class ExampleServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(
            dirname(__DIR__) . '/Config/menu.php',
            'menu.admin',
        );
    }
}

Use `register()`, not `boot()`

Menu config has to be merged before Laravel resolves the sidebar component. register() runs earlier in the lifecycle and is the safe place for mergeConfigFrom().

# โŒจ๏ธ Clear cached config

After editing menu config you have to clear Laravel's cached config so the new entries show up:

php artisan optimize:clear

The Krayin sidebar has two levels:

Level When it appears Used for
First level โ€” sidebar Always visible The top-level sections (Leads, Contacts, Settings, โ€ฆ)
Second level โ€” hover menu Appears on hover over a first-level item Sub-items grouped under that section

To create a nested entry, give it a dotted key that starts with the parent's key:

return [
    [
        'key' => 'catalogue',
        'name' => 'catalogue::app.menu.title',
        'route' => 'admin.catalogue.index',
        'sort' => 5,
        'icon' => 'icon-catalogue',
    ],
    [
        'key' => 'catalogue.examples',
        'name' => 'example::app.menu.examples',
        'route' => 'admin.examples.index',
        'sort' => 1,
    ],
    [
        'key' => 'catalogue.products',
        'name' => 'product::app.menu.products',
        'route' => 'admin.products.index',
        'sort' => 2,
    ],
];

catalogue becomes the parent (first level); catalogue.examples and catalogue.products appear on hover.

# ๐Ÿงช Verify

Reload your admin and look at the sidebar โ€” the new entry should be there with its icon. If it's missing, check in order:

  1. php artisan optimize:clear was run after the last config change.
  2. The route name in menu.php matches a real route โ€” php artisan route:list | grep examples should show it.
  3. mergeConfigFrom() is inside register(), not boot().
  4. The translation key in name resolves โ€” otherwise the label is empty and the entry can look invisible.

# ๐Ÿ“ Next steps

  • Access Control List โ€” once the menu links to your routes, gate them by permission so non-admins don't see entries they can't open.
  • Localization โ€” add the translation key your name references.