Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Menu Builder Laravel Package

infinety-es/nova-menu-builder

A Laravel Nova package that provides a drag-and-drop menu builder for creating, editing, and organizing custom navigation menus. Manage menu items, link types, and ordering through an intuitive admin interface, making it easy to build dynamic menus for your Nova app.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish assets

composer require infinety-es/nova-menu-builder
php artisan vendor:publish --provider="Infinety\MenuBuilder\MenuBuilderServiceProvider"
php artisan migrate

The publish step drops a config file (locales/labels) and two migrations — typically menus and menu_items. Read those migration files first: they tell you the exact column names (name, locale, link_type, url, route, order, parent_id, etc.) that every query and Blade template will depend on.

Register the tool in app/Providers/NovaServiceProvider.php:

public function tools()
{
    return [
        new \Infinety\MenuBuilder\MenuBuilder(),
    ];
}

First use case — a "Main navigation" menu. Open the new Nova tool, create a menu named main for your locale, then add a few links (route names or full URLs) and drag them into order. Consume it in a layout:

use Infinety\MenuBuilder\Models\Menu;

$menu = Menu::where('name', 'main')->first();

Then loop $menu->items in a Blade partial, recursing into children where they exist. Do this once in a layout and share it with a view composer (see below) — not inside every view.

That's the whole loop: manage in Nova → query the model → render yourself. There is no meaningful frontend runtime to learn; the package is essentially an admin UI over two tables.

Implementation Patterns

Share menus via a view composer, not per-view queries. Register a composer in a service provider and bind the resolved menu tree to your layout:

View::composer('layouts.app', function ($view) {
    $view->with('mainMenu', Menu::where('name', 'main')->first()?->items);
});

Seed menus for reproducible environments. Because the content lives in DB rows, treat menus like fixtures: a MenuSeeder that creates the main/footer records and their items means every dev and deploy gets identical nav, and content editors only adjust from there.

Render recursively with a partial. One partial that loops items and @includes itself for children keeps the markup identical at every depth and avoids the temptation to hardcode two levels:

{{-- resources/views/partials/menu.blade.php --}}
<ul>
@foreach ($items as $item)
    <li>
        <a href="{{ $item->url ?? route($item->route) }}">{{ $item->title }}</a>
        @if ($item->children->isNotEmpty())
            @include('partials.menu', ['items' => $item->children])
        @endif
    </li>
@endforeach
</ul>

Cache the rendered tree, keyed by menu name and locale. Menu content changes rarely but is read on every request. Cache::remember("menu.main.".app()->getLocale(), 3600, fn () => ...) around the query is the single highest-value integration step.

Drive item links by link_type. The package distinguishes URL vs. route (and possibly internal model) links. Centralise the resolution in a MenuLink helper or an accessor on the item model so your Blade stays a single {{ $item->href }} and a broken route name surfaces in one place instead of dozens of templates.

Multi-locale as separate menus. If the config exposes locales, create main per locale and pick by app()->getLocale() with a fallback to your default locale — don't try to build translation logic on top of individual items.

Editorial workflow tip: give content editors a Nova canSee/policy gate on the tool so nav changes don't require a deploy or developer involvement. That's the actual value proposition of this package — it replaces hardcoded nav arrays.

Gotchas and Tips

  • The last release was October 2019. It was built for Nova 2.x/3.x (Vue 2). On Nova 4+ the tool's compiled JS will likely fail to mount, or the Nova asset pipeline will reject it. Confirm the Nova version you're on before installing; if it breaks, you're forking Vue components rather than filing an issue.
  • It is effectively unmaintained. Budget for owning the migrations and the tool component. Pin the version and avoid dev-master; upstream fixes are not coming.
  • Ordering is a column, not the default query order. Always sort explicitly (orderBy('order')) — drag-and-drop writes the sort value, but nothing guarantees relationship queries return in that order.
  • Eager-load children to avoid N+1. A recursive partial on lazy-loaded children fires a query per item. Load items.children up front (or use a recursive eager-load) or your nav will quietly become the slowest part of the page.
  • Stored route names rot silently. If an item references a route name and you rename the route, route($item->route) throws at render time — on every page, in production. Prefer resolving in a try/catch with a logged warning, or favour stored URLs for volatile links.
  • Cache invalidation is on you. Saving a menu in Nova won't bust your Cache::remember. Add an observer on the menu/item models (or a Nova action hook) that flushes the relevant keys, otherwise editors will "save" changes that never appear.
  • Orphaned items after menu deletion. Deleting a parent item or menu may leave children/dangling parent_id rows depending on FK cascade settings in the migration. Check the migration and clean up in an observer if cascades aren't there.
  • Locale fallback is not automatic. Requesting a menu that doesn't exist for the current locale returns null; guard against ->first() returning null in your composer or your layout breaks on the first new locale.
  • Don't let the tool dictate your markup. The package's own rendering helpers are thin; write your own partial. It keeps you free to swap the package later, which — given the maintenance status — you should assume you eventually will. Keep the menu query behind a single repository/helper class so a future replacement only touches one file.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager