Rendering
Helper Rendering
echo $this->Menu->render($menu);You can also fetch the resolved active item and extract its path:
$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:
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:
echo $this->Menu->render('main', [
'singleActive' => true,
]);Breadcrumb Integration
$crumbs = $this->Menu->getBreadcrumbs('main');
$this->Menu->populateBreadcrumbs('main');
echo $this->Breadcrumbs->render();Or use the built-in breadcrumb renderer:
echo $this->Menu->renderBreadcrumbs('main', [
'renderer' => \Menu\Renderer\BreadcrumbRenderer::class,
]);Alternate Renderers
JSON export:
echo $this->Menu->render($menu, [
'renderer' => \Menu\Renderer\JsonRenderer::class,
'pretty' => true,
]);Bootstrap-flavored markup:
echo $this->Menu->render($menu, [
'renderer' => \Menu\Renderer\Bootstrap5Renderer::class,
]);Collapsible Bootstrap 5 sidebar — a vertical nav whose branches are Bootstrap collapse regions:
echo $this->Menu->render('sidebar', [
'renderer' => \Menu\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>:
echo $this->Menu->render('main', [
'renderer' => \Menu\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:
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:
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:
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 should extend Menu\Item\Item or implement Menu\Item\StateResetInterface for Menu::resetState() to restore their runtime 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) andaria-expandedfor branch items.