outl1ne/nova-menu-builder
Flexible menu builder for Laravel Nova. Create and manage hierarchical menus with an intuitive drag-and-drop interface, supporting custom links, model references, and nested items. Attach menus to models and render them on your frontend with ease. Everything you need for simple, powerful menu manage...
Architecture fit. This is a Nova tool, not a general Laravel package. Its value is entirely contingent on already running a licensed Laravel Nova v4/v5 instance; the package is a thin admin layer over two Eloquent models (Menu, MenuItem) plus a config file. That is a favorable fit for a TPM: the data model stays in the application's database, rendering stays in the application's Blade/API layer, and only the editing UI is vendor-supplied. The integration surface is unusually small and well-bounded — config/nova-menu-builder.php (menu_item_types, locales, menu_model, menu_item_model, nova_resource) plus the formatForDisplay() serialization method. Everything the frontend consumes is ours to shape, which keeps the admin tooling swappable and means the package is not load-bearing for customer-facing output.
Integration feasibility. High for a team already on Nova, low-to-moderate otherwise. Required work is bounded and well understood: composer require, publish provider assets, php artisan migrate, define menus, write one recursive Blade partial, and wrap data access in a helper or view composer with caching. Custom link types extend BaseMenuItemType and are registered in config — no vendor edits required. The credible scope is header, footer, utility/legal links, sidebar and documentation trees, mobile variants, and per-locale menus. All of these exercise existing capability, which makes the estimate defensible in a planning session.
Technical risk. The risks are concentrated and predictable rather than architectural:
composer update. MIT licensing means forking is legal; Laravel depth makes it practical but not free. Pin both majors explicitly.saved/deleted listeners on both models (item reorders fire saves), editors will report "my change didn't show up." This is the single most likely source of avoidable support load.config:clear/config:cache is re-run — no error, just a missing option in the builder modal.Key questions for the team:
composer.json constraint match our major today?Stack fit. Requires Laravel with Nova v4/v5; assumes MySQL/Postgres via standard migrations and no exotic runtime dependencies. The package provides admin CRUD and ordering UI; the application owns routing, rendering, caching, and the payload contract. Frontend consumption is either a recursive Blade partial over the formatted tree or a small controller endpoint for SPA/mobile/partner consumers. In both cases the serialized shape is ours to stabilize, which is the key property for multi-consumer roadmap items.
Migration path. Greenfield adoption is trivial: publish, migrate, create main-menu, seed it, render. Brownfield — replacing hardcoded nav markup or a hand-rolled menu table — should follow a shadow-then-cutover pattern: build menus in Nova behind a feature flag, render both old and new nav in staging, diff the output, then flip. Migrating existing hardcoded links into menus is manual work best done by the content owner, not engineering; budget it as an editorial task with an engineering-defined item-type set. If a prior menu table exists, a one-time mapping script into menus/menu_items is usually cheaper than parallel maintenance, but only worth it above a few dozen links.
Compatibility. Two hard constraints: the Nova major and PHP/Laravel versions implied by the package's composer.json. Everything else is soft. Custom models and the Nova resource are overridable via config, so soft deletes, accessors, stricter policies, and tenant scoping can be layered without forking. The remaining compatibility surface is our own: slug casing (main-menu vs main_menu vs mainmenu will collide in practice — standardize in the helper and consider a unique index in a follow-up migration), and the actual shape returned by formatForDisplay(), which varies by item type and should be verified by dumping a real tree rather than trusting the README.
Sequencing. Recommended order, each step independently shippable:
menu_item_types and locales in config; clear config cache.LinkItem/TextItem and one menu (main-menu) with a seeded tree — validate the end-to-end loop before customizing.MenuPolicy and lock down access; define the editor role.getRules() and getDisplayValue(), plus the delete-behavior decision from the key questions.Do not sequence custom item types or locales before the caching and policy layer — both amplify the cost of getting freshness and permissions wrong.
Maintenance. Low steady-state, spiky at Nova upgrades. The codebase we own is small: one partial, one helper, a policy, a seeder, optionally one or two BaseMenuItemType subclasses, and config. The real maintenance obligation is the Nova coupling — assign a named owner and schedule upgrade evaluation as planned work each time Nova majors. Keep vendor code untouched (use the config override triangle: nova_resource/menu_model/menu_item_model, getDisplayValue(), and model event listeners) so updates remain cheap. The package is MIT and forkable, which limits lock-in risk, but a thin community (242 stars, zero dependents) means we should not rely on upstream for non-trivial fixes.
Support. Support load is predictable and mostly editorial, not technical. Expect two recurring classes of ticket: "my change didn't appear" (cache invalidation — solved structurally by the event listeners, not by training) and "this link is broken" (orphaned model references or free-URL rot — solved by policy on link typing and delete behavior). Ship a short editor runbook covering drag-and-drop semantics — deleting or moving a parent mutates order and parent_id destructively, and manual DB edits leave the builder showing a stale tree until refresh. Require all ordering changes to go through the UI. Escalation to engineering should be limited to new item types and rendering changes; that boundary is worth stating explicitly so it is enforceable.
Scaling. Rendering scales well if the formatted tree is cached rather than re-queried per request. The naive $menu->items → ->children chain N+1s badly on deep trees; caching the formatted array sidesteps this and is the preferred pattern over per-request eager loading. Beyond that the volume characteristics are trivial — menus are small, low-cardinality data. The genuine scaling limits are functional, not performance: the package does not provide publish windows, scheduling, A/B or segment-based navigation, merchandising slots, audit trails, or tenant isolation. If the roadmap calls for any of those, we will be building a bespoke system on top of a thin base, and that should be scoped as a separate project rather than an extension of this integration.
Failure modes. Ranked by likelihood and blast radius:
getDisplayValue() fallback; without one, produces customer-visible 404s.config:cache ran against stale config. No error surfaces; the option simply disappears from the modal.getRules() referencing a data. key that getFields() does not write; items save successfully but render blank. Debug at the model layer, not in the Nova UI.Ramp-up. Fast for a Laravel developer familiar with Nova: roughly a day to first rendered menu, two to three days to a hardened setup (caching, event listeners, policy, seeded menus, editor runbook). A developer new to Nova should add a day. Editorial ramp-up is a short walkthrough plus the runbook — the drag-and-drop UI is the least of the training burden; the concepts that actually need teaching are link typing (model reference vs. free URL), locale scoping if enabled, and the fact that destructive tree edits take effect immediately for visitors once cache clears. Recommended exit criteria for the pilot: header and footer both editor-managed, cache busting verified end to end, policy enforced, and zero engineering involvement in a routine navigation change.
How can I help you explore Laravel packages today?