Sitelet https://dereuromark.github.io/cakephp-menu/guide/rendering
Skip to content

Rendering ​

Helper Rendering ​

php
echo $this->Menu->render($menu);

You can also fetch the resolved active item and extract its path:

php
$current = $this->Menu->getCurrentItem('main');
$path = $current ? $this->Menu->extractPath($current) : [];

Hiding Empty Branches ​

When a resolver hides all of a branch's children (for example access filtering), the parent would otherwise render as an empty dropdown. Enable hideEmptyBranches to skip any branch with no visible, renderable descendants:

php
echo $this->Menu->render('main', [
    'hideEmptyBranches' => true,
]);

This looks at child visibility, not the depth cutoff: a branch with visible children is still a valid top-level entry when its submenu is truncated by depth, so it is kept.

Single Active Item ​

When several items match the current URL (e.g. a parent and a child both pointing at the same route), getActiveItem() and breadcrumbs follow the first match in document order. Enable singleActive to keep only the best match active; the deepest visible item that actually renders (items hidden, or under hidden ancestors, are skipped), breaking ties by document order; so the active trail is unambiguous:

php
echo $this->Menu->render('main', [
    'singleActive' => true,
]);
php
$crumbs = $this->Menu->getBreadcrumbs('main');
$this->Menu->populateBreadcrumbs('main');

echo $this->Breadcrumbs->render();

Or use the built-in breadcrumb renderer:

php
echo $this->Menu->renderBreadcrumbs('main');

renderBreadcrumbs() always uses BreadcrumbRenderer. Pass a subclass name or instance as renderer to customize it. Cake's attributes and separator belong in your separate $this->Breadcrumbs->render(...) call after populateBreadcrumbs().

Alternate Renderers ​

JSON export:

php
echo $this->Menu->render($menu, [
    'renderer' => \CakeMenu\Renderer\JsonRenderer::class,
    'pretty' => true,
]);

Bootstrap-flavored markup:

php
echo $this->Menu->render($menu, [
    'renderer' => \CakeMenu\Renderer\Bootstrap5Renderer::class,
]);

Collapsible Bootstrap 5 sidebar; a vertical nav whose branches are Bootstrap collapse regions:

php
echo $this->Menu->render('sidebar', [
    'renderer' => \CakeMenu\Renderer\Bootstrap5SidebarRenderer::class,
]);

The branch containing the active item is expanded (collapse show, aria-expanded="true"), all other branches start collapsed, and the active leaf gets the active class plus aria-current="page". Each branch is wired to its collapse element through a unique id, so it works with the standard Bootstrap bundle and needs no custom JavaScript. Item and submenu attributes from the menu definition are preserved on the <li> and nested <ul>.

Full Bootstrap 5 navbar; the complete <nav> chrome (brand, responsive toggler, and the collapsible navbar-nav with dropdowns), rather than just the inner <ul>:

php
echo $this->Menu->render('main', [
    'renderer' => \CakeMenu\Renderer\NavbarRenderer::class,
    'brand' => 'MyApp',
    'brandUrl' => '/',
    'expand' => 'lg',                 // navbar-expand-lg (collapse breakpoint)
    'theme' => 'bg-body-tertiary',
    'containerClass' => 'container-fluid',
    'collapseId' => 'navbarNav',      // set per navbar if you have more than one on a page
]);

NavbarRenderer generates the toggler/collapse wiring (matching data-bs-target, id, and aria-controls) for you. For just the <ul class="navbar-nav"> to drop inside your own navbar markup, use Bootstrap5Renderer directly.

A branch that only groups children (placeholder link #/none) renders a single toggle. A branch that also has a real URL stays navigable: it renders the link plus a separate collapse toggle button (data-bs-target), so the destination is reachable and its link attributes are kept.

Besides the shared activeClass, currentAsLink, addAriaCurrent and hideEmptyBranches options, the sidebar exposes framework-specific keys (idPrefix, navClass, toggleClass, collapseClass, expandedClass, toggleAttribute, caret, …). They all default to Bootstrap 5 and can be overridden to target Bootstrap 4 or another setup without subclassing; for example toggleAttribute => 'data-toggle', expandedClass => 'is-open'.

TIP

See the complete list with defaults in the Renderer Options reference.

You can override templates per render call:

php
echo $this->Menu->render($menu, [
    'templates' => [
        'menuWrapper' => '<nav><ul{{attributes}}>{{items}}</ul></nav>',
    ],
]);

Renderer Options ​

Every renderer option; class names, ARIA flags, depth limits, and the Bootstrap-specific keys; is documented with its default value in the Renderer Options reference. Pass options per render call, as constructor config, or via setConfig().

Bootstrap5Renderer overrides some StringTemplateRenderer defaults (e.g. ancestorClass → active, branchClass → dropdown, nestedMenuClass → dropdown-menu) and adds link-level keys (linkClass, childLinkClass, toggleClass, toggleAttribute, toggleValue) so Bootstrap 4 or other setups work without subclassing.

WAI-ARIA menu roles ​

By default the renderers emit aria-current/aria-expanded/aria-label. For the full WAI-ARIA menu pattern, opt in with roles => true:

php
echo $this->Menu->render('main', ['roles' => true]);

This adds role="menubar" (root) / role="menu" (nested) on the lists, role="none" on items, role="menuitem" (plus aria-haspopup="true" on branches) on links, and role="separator" / role="presentation" on dividers / headers. It is off by default so existing markup is unchanged.

Auto-translation ​

If your labels are message keys or untranslated source strings, enable translate to pass them through Cake's default translator before escaping:

php
echo $this->Menu->render('main', [
    'translate' => true,
]);

This applies to normal items and headers. It does not translate trusted markup fields such as before, after, raw, icon, or badge.

Good to know ​

Escaping

Item labels are escaped by default. before, after, raw, and the icon/badge markup are treated as trusted; escape or cast dynamic values yourself.

State is restored automatically

Each helper render (or request-state lookup) applies resolvers temporarily and restores the original active, visible, and expanded item state afterward, so a registered menu renders safely many times per request. Custom item classes implement the runtime setters and resetState() on CakeMenu\Item\ItemInterface to restore their authoring defaults.

  • String URLs and array URLs are both supported; active matching is automatic and uses both array and string resolvers.
  • The default renderer emits aria-current="page" for the active item (link or label) and aria-expanded for branch items.

Released under the MIT License.