# 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
# Menu item schema
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
# ๐งฑ Menu levels
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:
php artisan optimize:clearwas run after the last config change.- The route name in
menu.phpmatches a real route โphp artisan route:list | grep examplesshould show it. mergeConfigFrom()is insideregister(), notboot().- The translation key in
nameresolves โ 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
namereferences.
โ Getting Started Assets โ