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

Config Knp Menu Bundle Laravel Package

jbouzekri/config-knp-menu-bundle

A Symfony bundle that brings configuration-driven menus to KnpMenuBundle, letting you define navigation menus in YAML instead of PHP. It simplifies menu setup and maintenance while leveraging KnpMenu’s rendering, extensions, and Symfony integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites: the bundle is a configuration layer on top of KnpMenuBundle, not a replacement. Install and configure knp-menu-bundle first (Twig helper, knp_menu.menu_provider), otherwise there is nothing for this package to feed.

composer require jbouzekri/config-knp-menu-bundle

Register it in config/bundles.php (Flex may not do this automatically given the low install count):

Jbouzekri\ConfigKnpMenuBundle\ConfigKnpMenuBundle::class => ['all' => true],

First config file — in a Symfony Flex app, config/packages/config_knp_menu.yaml (pre-Flex: app/config/config.yml under the bundle's root key, commonly config_knp_menu:):

config_knp_menu:
    menus:
        main:
            children:
                home:
                    label: 'Home'
                    route: 'app_home'
                catalog:
                    label: 'Catalog'
                    uri: '#'
                    children:
                        products:
                            label: 'Products'
                            route: 'app_product_index'

First use case: delete the hardcoded <ul> from your base.html.twig and replace it with:

{{ knp_menu_render('main', {'currentClass': 'active'}) }}

Where to look first (in this order):

  1. vendor/jbouzekri/config-knp-menu-bundle/README.md — the only real docs.
  2. DependencyInjection/Configuration.php — this is the authoritative schema. Every key (route, uri, label, attributes, childrenAttributes, extras, display, …) is defined there. When the README is stale, trust this file.
  3. bin/console config:dump-reference config_knp_menu — prints the same tree with defaults, without reading source.
  4. DependencyInjection/ConfigKnpMenuExtension.php — shows how bundles' YAML files are discovered and merged, which is the actual selling point of the package.

Then run bin/console cache:clear and check bin/console debug:config config_knp_menu.

Implementation Patterns

Per-bundle fragments (the reason to use this package). Each bundle ships its own menu YAML (Resources/config/config_knp_menu.yaml or whatever convention the extension loads — confirm in the extension) and the app-level file contributes the app-specific items. The DI extension merges everything into one menu tree. Prefix item names per bundle (shop_products, admin_users) so two bundles can never clash on a key.

Nested/dropdown menus work through children; the parent typically gets uri: '#' and CSS attributes:

catalog:
    label: 'Catalog'
    uri: '#'
    attributes:
        class: 'dropdown-toggle'
    childrenAttributes:
        class: 'dropdown-menu'
    children:
        products:
            label: 'Products'
            route: 'app_product_index'
            routeParameters: { page: 1 }

Styling and icons are done via attributes / linkAttributes / extras. Use knp_menu_render('main', {'template': 'menu/main.html.twig'}) when the default markup isn't enough.

Structured metadata in extras (new in 7.1, PR #26). extras now accepts nested maps and lists instead of only flat scalars, so per-item presentation data that used to require a compiler pass or an event subscriber can live in YAML:

products:
    label: 'Products'
    route: 'app_product_index'
    extras:
        icon: { name: 'fa-box', style: 'solid' }
        badge: { text: 'New', variant: 'success' }
        analytics: { event: 'nav_click', params: ['section', 'catalog'] }

Read it in a custom Twig template with item.getExtra('icon').name / item.getExtra('badge').text (twig attribute() if you prefer explicit access). Anything you can read back through getExtra() — including nested arrays — is fair game, so this is the natural home for icons, badge copy, data-attribute maps, and analytics payloads. Keep leaf-level extras keys namespaced (icon, badge, analytics) rather than dumping a dozen top-level keys, and don't put behaviour here: routing, visibility, and access checks still belong in KnpMenuBundle's own options.

Mixed static + dynamic menus. The config-defined tree is fine for the stable 90%, but "My account" items depend on the logged-in user. Don't try to express that in YAML — subscribe to KnpMenuBundle's ConfigureMenuEvent (or write a small compiler pass) and mutate the menu after it's built:

public function onConfigureMenu(ConfigureMenuEvent $event): void
{
    if ($event->getMenu()->getName() !== 'main' || !$this->security->getUser()) {
        return;
    }
    $event->getMenu()->addChild('account', [
        'route' => 'app_account',
        'extras' => ['icon' => ['name' => 'fa-user', 'style' => 'solid']],
    ]);
}

Note the extras you pass to addChild() at runtime are plain KnpMenu extras — the 7.1 nesting support applies to the YAML path, so keep the array shape identical between the two sources if the same template renders both.

Environment tweaks. Because it's plain Symfony config, when@dev: blocks let you add a "Debug" menu entry only in dev without touching production output.

Testing. Inject Knp\Menu\Provider\MenuProviderInterface, call get('main'), and assert on getChildren() / getLabel() / getUri() / getExtra('icon')['name']. This exercises the YAML merge without booting a browser.

Gotchas and Tips

  • Config is compiled. YAML edits do nothing until you clear the container cache (bin/console cache:clear, or cache:pool:clear cache.global_clearer in dev if opcache/FPM is involved). Half of "the bundle is broken" reports are stale config cache. This applies doubly to extras shape changes — an old compiled container keeps serving the old flat extras array, which is a confusing getExtra('icon')['name'] on null.
  • Merge semantics are the sharp edge. Deep-merging children arrays across bundles is not obviously defined — some extensions use array_merge_recursive, some replace. Read ConfigKnpMenuExtension before relying on a bundle overriding an app-level home: item; a same-named child may be replaced or duplicated depending on implementation. The same question now applies inside extras: verify empirically whether a bundle fragment merging extras: {icon: {...}} into an existing item deep-merges the nested map or replaces the whole extras key.
  • Nested extras needs 7.1+. On earlier 7.x versions nested arrays under extras were not preserved, so an upgrade is required before moving structured metadata out of a subscriber. Pin the constraint (^7.1) rather than ^7.0 and check composer show jbouzekri/config-knp-menu-bundle on CI, or you'll get a flat/empty extras array depending on the machine.
  • Routes are resolved when the menu is built, not at boot. A typo in a route: key throws RouteNotFoundException during rendering, i.e. a 500 on every page that renders the nav. Grep your config for routes when you delete/rename a controller action.
  • Menu visibility is not access control. If the schema supports display / roles, understand it as cosmetics only — hidden items are still routable. Enforce with access_control / voters on the actual routes.
  • display: false still keeps the item in the tree (useful for isCurrent/breadcrumb maths) but a hidden parent hides its children. Prefer omitting the item when you don't need it at all.
  • Duplicate YAML keys are silent. Two bundles defining children.home in the same map — the last one wins with no warning. Prefix names.
  • Ordering follows YAML order, not alphabetical. If you need explicit ordering across bundles, check whether the schema exposes a priority/weight key; otherwise ordering across merged files is merge-order-dependent and fragile.
  • Translations: label values are passed through the translator. Set extras: {translation_domain: 'navigation'} (or the equivalent top-level key) rather than hardcoding English labels.
  • Maintenance risk, slightly improved. 2025's 7.1 lands a real contributor PR (#26, thanks @Baachi) after a long gap, so the project isn't abandoned — but it's still a one-maintainer package with near-zero dependents and stars. Verify it against your Symfony/KnpMenuBundle major (KnpMenuBundle 3.x changed builder signatures and menu provider wiring). If it drifts again, vendoring is cheap: copy the DependencyInjection/ and menu-building classes into src/Menu/ and drop the bundle registration — you keep the YAML workflow and lose the maintenance question. The extras nesting is a couple of lines in the normaliser, so a vendored copy survives that feature gap easily.
  • Debugging checklist: config:dump-reference (key exists?) → debug:config config_knp_menu (did my file get merged? is extras the shape I expect?) → debug:container --tag=knp_menu.provider → render the menu in a controller and dump($menu) / dump($menu->getChild('products')->getExtra('icon')).

Overall: a genuinely nice idea (declarative menus merged across bundles) with a thin, barely-maintained codebase — though 7.1 proves someone other than the author can still get a patch in. Worth it for a small, stable nav tree in a monorepo of bundles; the nested-extras support removes one common reason to reach for an event subscriber. Budget an afternoon to read the extension and the Configuration class first, and keep a fallback plan to inline the ~few hundred lines if it drifts from KnpMenuBundle.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console