smorken/menu
Smorken Menu is a Laravel package for building and managing navigation menus, with support for storing menu items and rendering them in your application's views.
Treat the repository itself as the documentation. With zero stars and zero dependents, there is no community knowledge base to fall back on, so the first 30 minutes should be spent reading source rather than guessing at the API.
Install and inspect the package metadata.
composer require smorken/menu
Open vendor/smorken/menu/composer.json and check:
extra.laravel.providers and extra.laravel.aliases entries (auto-discovery vs. manual registration),Register manually if discovery is absent.
Add the service provider to config/app.php and, if a facade is exposed, an alias. Confirm the exact class names from src/ before copying anything from a blog post.
Publish and review config.
php artisan vendor:publish --provider="Smorken\Menu\MenuServiceProvider"
Verify the provider name from the package source first. Read the published config top to bottom; it usually reveals whether menus are config-driven, database-driven, or both.
Run migrations only if the package ships them.
If there are menu/menu-item tables, run php artisan migrate and inspect the schema. Note foreign keys and ordering columns — they determine how you will sort and nest items.
First use case: a header navigation with active-state highlighting. Define one menu with two or three route-based items, render it in your main layout, and confirm that the current route receives an active class. This exercises the core value of the package: route awareness, nesting, and rendering. Do not start with a 60-item admin sidebar; start with the smallest menu that proves the integration works.
The key question to answer before building on it: is the package a builder (you define items in PHP and it renders them) or a store (menus live in the database and are edited by users)? The day-to-day workflow differs substantially.
Prefer route names over hard-coded URLs. The main reason to use a menu package is that it can resolve route names, apply parameters, and detect the active item. Define items with route names and parameter arrays wherever possible, and reserve raw URLs for external links. This keeps menus working across localization prefixes and domain changes.
Separate menu definition from menu rendering. Define each menu in a dedicated place — a config file, a service provider, or a small MenuRegistry class — and render it from Blade. This gives you one place to change navigation and one place to change markup. A typical pattern:
// app/Support/Menus.php
Menu::make('main', function ($menu) {
$menu->add('Dashboard', ['route' => 'dashboard']);
$menu->add('Projects', ['route' => 'projects.index']);
$menu->add('Admin', ['route' => 'admin.index'])
->visible(fn () => Gate::allows('access-admin'));
});
The exact method names (add, route, visible, children) must be confirmed against the source. The pattern — build a tree, attach metadata, render recursively — is what matters.
Render with a recursive Blade partial or component. Menu packages almost always expose either a helper, a facade render method, or a Blade directive. Wrap whichever exists in your own component so the rest of the application never depends on the package's markup:
{{-- resources/views/components/nav.blade.php --}}
<ul class="nav">
@foreach ($items as $item)
<li class="{{ $item->isActive() ? 'active' : '' }}">
<a href="{{ $item->url() }}">{{ $item->label() }}</a>
@if ($item->hasChildren())
<x-nav :items="$item->children()" />
@endif
</li>
@endforeach
</ul>
This adapter layer is the most valuable thing you can build. If the package is abandoned, you replace one component instead of every layout.
Apply authorization at definition or render time, never in the database alone. Filter admin items with Gate::allows() or @can so links never appear for unauthorized users. Hiding a link is not authorization, but showing it is a UX leak. Keep the policy check next to the menu item so it is auditable.
Use one menu per navigation region. Name them explicitly (main, footer, account, admin-sidebar) rather than sharing a single global menu. Independent menus are easier to cache, test, and override per layout.
Cache the rendered tree, not the whole page, and only for anonymous users. Menu structure changes rarely. If the package is database-backed, cache the tree or the rendered HTML with a key that includes the user's role or permission set. Never cache an auth-filtered menu under a global key.
Test menu contracts. Assert that a route appears for an authorized user, disappears for a guest, and receives the active class on the expected route. These are cheap feature tests that catch the most common regressions: renamed routes and broken active-state logic.
composer.json, fork it internally if you rely on it, and budget for replacement. Check the last commit date and the illuminate/* constraints before committing to it.Menu alias is common and can clash with other packages or your own classes. If you see "class not found" or unexpected behavior, check config/app.php aliases and prefer injecting the underlying class.php artisan config:cache will fail or silently serialize incorrectly. Use class strings or move definitions into a service provider.admin*) will highlight "Admin" on "Admin/Users/Edit" — usually desired — but will also match unrelated routes like "administrator". Test wildcard behavior explicitly, and watch trailing slashes and query strings.projects.show without an id will throw when resolved. Either supply parameters or skip parameterized routes in global navigation.{{ }} for labels and only reach for {!! !!} for deliberately trusted icon markup. Recursive partials are a common XSS entry point when labels are user-editable.src/: a bindable renderer/presenter interface, a swappable model via config, published Blade views, and events fired when a menu is built. These are your escape hatches.dd($menu->toArray()) or the package's equivalent; run php artisan route:list to confirm route names; clear config, route, and view caches after changing menu definitions; and check whether the service provider is actually registered with php artisan about.How can I help you explore Laravel packages today?