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 Laravel Package

norman-huth/nova-menu

A Laravel Nova tool for creating and managing site menus from the Nova admin panel. Build hierarchical, drag-and-drop menus, link to Nova resources or custom URLs, and render them in your frontend with a simple API.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install into a Nova app (Nova 4/5 assumed — check the README matrix before committing):

    composer require norman-huth/nova-menu
    
  2. Publish and migrate. This is a database-backed menu builder, so nothing works until the tables exist:

    php artisan vendor:publish --provider="NormanHuth\NovaMenu\NovaMenuServiceProvider"
    php artisan migrate
    

    Publish before migrating; the config file controls table names and (typically) the set of fields available on a menu item.

  3. Register the Nova resources. Most Nova packages in this family do not auto-register their resources reliably across Nova majors. Add the packaged Menu / MenuItem resources to NovaServiceProvider::resources() (or verify the package registers them itself — look in the service provider's boot()).

  4. Look here first, in order:

    • config/nova-menu.php — models, table names, item field definitions, cache settings.
    • The service provider — what is auto-registered vs. what you must wire up manually.
    • The Menu model — this is your rendering entry point.
  5. First use case: create a "Main navigation" menu in the Nova UI, add a handful of items (label + URL/route), then render it in your Blade layout:

    {{-- in resources/views/layouts/app.blade.php --}}
    {!! menu('main') !!}
    

    Confirm the exact helper/directive name in the README — this package exposes the rendered menu through a helper and/or a Blade component, and the signature differs between releases.

  6. Sanity check: if the menu renders empty, you almost certainly have (a) the wrong slug, (b) a menu that exists but is not marked active/enabled, or (c) a stale cache. Hit the empty-menu path deliberately on day one so you recognise it later.

Implementation Patterns

Multi-menu by slug. The intended model is one row per menu (main, footer, sidebar, legal), each with a slug you query by. Treat the slug as a stable contract in your Blade templates and never let editors rename it — add a config-level enum of allowed slugs if the UI doesn't enforce one.

Layout integration via a view composer, not a helper call in the layout. Calling the helper directly in app.blade.php means every page triggers the same query. Wrap it:

View::composer('layouts.partials.nav', function ($view) {
    $view->with('menu', \Cache::remember('menu.main', 3600, fn () => menu('main')));
});

Then {!! $menu !!} in the partial. This gives you one place to invalidate from.

Cache invalidation on edit. Hook the packaged models' saved/deleted events in a small observer and forget the matching cache key. This is the single highest-value integration you can write; without it, editors will report "my changes don't show up" and you'll be clearing caches manually for the life of the project.

Seeding for reproducible deploys. Menus edited in production are content, and content doesn't live in git. Pick one: seed a baseline menu in a seeder for fresh environments, and treat production edits as data. Don't try to seed and hand-edit the same menu — you'll get duplicates.

Custom item types. The item fields are config-driven. For anything beyond label/URL — e.g. a link to a CMS page, a route with parameters, a "mega menu" column — extend the config's field list rather than forking the package, and resolve the final URL in your own presenter/renderer rather than in the database. Storing a resolved URL means it goes stale the moment a slug changes; storing a route name + params survives.

Authorization. The Nova resources are editable by anyone with Nova access by default. Add policies if "who can restructure the site navigation" is narrower than "who can log into Nova" — it usually should be.

Localisation. If the site is multilingual, run one menu per locale (main.de, main.en) rather than items with translated labels. It keeps the tree structure independent and lets translators work without stepping on each other.

Testing. Snapshot the rendered HTML for a fixture menu so a package upgrade that changes markup fails loudly instead of silently breaking your CSS selectors.

Gotchas and Tips

  • Archived. This package is archived and has zero dependents, so there is no upstream fix path. Pin the exact version in composer.json, and if you need a change, plan to fork or vendor it rather than waiting. Budget for the migration path before you build deep dependencies on it.
  • Nova version drift is the real risk. Nova's resource/tool APIs change between majors, and an archived package won't follow. Verify the install against your exact Nova version first; that is the go/no-go test.
  • Publish before migrate. Running migrate first gives you the package's default tables, and a later config edit (custom table names) leaves the database out of sync with the config with no error — just a menu that silently returns nothing.
  • Table-name collisions. Configurable table names mean this package can be installed alongside a homegrown menu table; pick a distinct prefix up front if you're migrating existing navigation in.
  • Empty render is not an error. Most implementations return an empty string for a missing slug. This hides typos in production. Log a warning in your wrapper when the result is empty in non-production, or assert on it in tests.
  • Escape vs. raw output. You'll be using {!! !!} to render the generated markup. That is only safe because the package escapes item labels — if you add a custom field (a rich-text description, an icon SVG), verify escaping yourself before printing it raw.
  • Ordering / nesting. Drag-and-drop ordering is persisted, but the renderer may or may not rebuild the tree depending on how deep the item model goes. If you see a flat list where you expected children, it's a nesting-config issue, not a CSS one.
  • Cache keys and multi-tenancy. If the app is multi-tenant or multi-locale, the cache key must include the tenant/locale, or the first tenant's menu is served to everyone. This is the classic bug with this class of package.
  • Debugging checklist when something looks wrong: (1) php artisan cache:clear, (2) confirm the slug exists in the DB, (3) confirm the resources are actually registered in Nova, (4) dump the raw model rather than the rendered helper to separate data issues from rendering issues.
  • Extension points worth knowing: the config's field definitions (extend without forking), the models (swappable via config — subclass to add presenter methods), and Nova resource methods (override fields/tools if you need a narrower editing UI). Prefer overriding via config and subclassing over editing vendor files; vendor edits vanish on the next composer update and, with an archived package, that update is exactly when you'll be firefighting.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect