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

Ux Menus Laravel Package

krystal-sf/ux-menus

A Laravel package for defining and rendering application menus. Build nested navigation structures, output them in Blade views, and keep menu definitions reusable and consistent across your app's layouts.

View on GitHub
Deep Wiki
Context7

Getting Started

First, calibrate expectations: krystal-sf/ux-menus is a Symfony UX package, not a Laravel one. There is no config/app.php provider entry, no Blade directives, no artisan command, and no Illuminate\Contracts implementation. As a Laravel developer you have two realistic paths:

  1. Borrow the pattern, not the package — copy the tagged-menu idea into a Laravel service provider (this is the recommended path for most teams).
  2. Install it in a Laravel app anyway — possible only if you already run twig/twig (e.g. via rcsi/twigbridge), because rendering is Twig-component based.

Minimal steps to evaluate:

composer require krystal-sf/ux-menus

Then read, in this order:

  • README.md — how a menu is declared and rendered (this package has effectively 0 stars and 0 dependents, so the README is your only real documentation).
  • composer.json — look at type and extra.symfony.bundle / extra.symfony.flex. If it is a bundle, it relies on Flex auto-registration, which Laravel does not have.
  • src/DependencyInjection/ — find the compiler pass. This is the heart of the package: it scans for a service tag (likely something like ux_menu.menu or krystal.ux_menu) and collects tagged services into a menu registry.
  • src/Twig/ — find the extension or Twig component class that turns the registry into renderable output.

First use case: an admin sidebar or top-nav where each menu item is a small service class. You tag the service, give it a menu and priority attribute, and the renderer sorts and renders them. In Laravel the equivalent first use case is a tagged collection resolved in a MenuServiceProvider and handed to a Blade component — see below.

Implementation Patterns

Understand the tag contract before anything else

The whole package is a compiler pass around Symfony's service tagging. In a Symfony app the shape is roughly:

# services.yaml
App\Menu\DashboardItem:
    tags:
        - { name: 'ux_menu.item', menu: 'admin', priority: 10 }

The compiler pass collects every tagged service, groups by the menu attribute, sorts by priority, and exposes a MenuInterface / collection to the renderer. Verify the exact tag name from the compiler pass source — it is the single most important string in the package and it is not guaranteed to be documented.

Laravel equivalent: tagged menu items

Laravel has the same primitive, just spelled differently. Register items with a container tag and resolve them in a builder:

// App\Providers\MenuServiceProvider.php
public function register(): void
{
    $this->app->singleton(MenuRegistry::class, function ($app) {
        return new MenuRegistry(
            $app->tagged('menu.admin'),
            $app->tagged('menu.account'),
        );
    });
}

public function boot(): void
{
    $this->app->tag([DashboardMenuItem::class], 'menu.admin');
    $this->app->tag([ProfileMenuItem::class], 'menu.account');
}
final class MenuRegistry
{
    public function __construct(private iterable $admin, private iterable $account) {}

    public function admin(): array
    {
        $items = iterator_to_array($this->admin);
        usort($items, fn ($a, $b) => $b->priority() <=> $a->priority());
        return $items;
    }
}

This reproduces the package's behavior without the Symfony coupling, and is the pattern I would ship in a Laravel codebase nine times out of ten.

Interface-first menu items

The cleanest part of the design to steal is the contract. Every item implements a small interface (getLabel(), getRoute()/getUrl(), isVisible(), getPriority()). isVisible() matters more than it looks — it lets you keep authorization logic next to the item instead of scattering @can checks in the template.

Rendering

In Symfony the renderer is a Twig component or a {{ ux_menu('admin') }}-style function. In Laravel, wrap the registry in an anonymous Blade component:

{{-- resources/views/components/menu.blade.php --}}
<ul {{ $attributes->merge(['class' => 'menu']) }}>
    @foreach ($items as $item)
        @if ($item->isVisible())
            <li><a href="{{ $item->getUrl() }}">{{ $item->getLabel() }}</a></li>
        @endif
    @endforeach
</ul>

Caching

Menus are static per deploy. Cache the resolved array in a Cache::rememberForever() keyed on the menu name and config('app.version'), and flush on deploy. Without this you rebuild the item graph on every request.

Gotchas and Tips

  • Abandonment risk is real. No stars, no dependents, and a single-org namespace. Pin the exact version in composer.json, vendor the compiler pass logic if you must use it, and do not build a critical navigation layer on top of it. Treat it as a reference implementation.
  • The tag name is undocumented at your peril. A typo in the name: attribute fails silently — the item simply never appears, with no exception. Debug in Symfony with bin/console debug:container --tag=ux_menu.item. In Laravel the equivalent is php artisan tinker plus app()->tagged('menu.admin') to confirm the collection is non-empty.
  • Priority ordering is easy to invert. Symfony's priority convention is higher first, but plenty of packages sort ascending. Read the usort/uasort line in the compiler pass rather than assuming — wrong direction produces a menu that looks almost right, which is the worst kind of bug.
  • Flex auto-registration is invisible magic. In a Symfony app the bundle registers itself because of extra.symfony.flex; if you copy the package into a non-Flex skeleton, nothing works and no error tells you why. In Laravel, TwigBridge will not auto-register Twig namespaces either — you must add the bundle's view path to config/view.php or the bridge config.
  • isVisible() is the extension point. If you need per-user menus, override the visibility policy rather than subclassing the registry — subclassing breaks the compiler-pass ordering and is the usual source of "menu items duplicate" bugs.
  • Watch the request scope. If the registry is a singleton and menu items hold the current Request or user, you will leak state between queued jobs and long-running workers (Octane especially). Keep items stateless and inject Request at render time.
  • No tests, assume nothing. With zero dependents there is no community validation of edge cases: empty menus, duplicate priorities, missing menu attribute. Write a feature test that asserts the rendered menu order on day one — it is cheaper than debugging a silently empty nav in staging.
  • Extension points worth knowing: a custom renderer (Twig extension / Blade component), a MenuType-style builder class for grouping, and the visibility callback. Anything beyond those three is likely a fork.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib