Demonstrate Menu (multi-level).
View this pattern on the live demo site
Nested header navigation used by the site chrome.
{% comment %}
Purpose: Renders a responsive multi-level navigation (Bootstrap-style navbar with dropdowns). Supports up to three levels when max_menu_depth is 3 (top bar, first dropdown, nested submenu).
Escaping: Class inputs used inside the item loop are normalized once via `var ... | escape` above. Other class strings use `default | escape` at their single output.
Inputs:
menu - Menu object from {% menu %} (iterable; items use name, href.value, items for children).
max_menu_depth - Maximum depth to render: 2 (top + one dropdown) or 3 (nested submenu under a second-level item). Clamped to 2–3. Defaults to 2.
nav_classes - Classes on the outer <nav>. Default: minimal navbar + expand breakpoint.
nav_id - Optional id on <nav>.
aria_label - Accessible name for the navigation region. Defaults to 'Main'.
show_navbar_toggler - When true, outputs toggler and collapse wrapper. Defaults to true.
mobile_menu_label - Label beside the toggler on small screens. Defaults to 'Menu'.
mobile_menu_label_classes - Classes on the mobile label span. Defaults to 'navbar-text d-lg-none'.
container_classes - Inner wrapper. Defaults to 'container'.
ul_classes - Root <ul> classes. Defaults to 'navbar-nav'.
nav_link_classes - Top-level leaf links when show_navbar_toggler is false, or desktop leaf links when true.
nav_link_classes_mobile - Top-level leaf links on small screens when show_navbar_toggler is true.
dropdown_toggle_classes - Top-level <a> that opens a dropdown.
dropdown_menu_classes - First-level dropdown <ul>.
dropdown_item_classes - Second-level links that are leaves (no third level or max_menu_depth is 2).
nav_item_leaf_mobile_classes - <li> for top-level leaves on small screens when toggler is used.
nav_item_leaf_desktop_classes - <li> for top-level leaves on large screens when toggler is used.
nav_item_dropdown_classes - <li> wrapping a top-level dropdown.
nested_submenu_li_classes - <li> wrapping a second-level item that has a nested (third-level) menu. Defaults to 'dropend' (Bootstrap nested dropdown).
nested_dropdown_toggle_classes - <a> for a second-level parent with children (opens third level). Defaults to 'dropdown-item dropdown-toggle'.
nested_dropdown_menu_classes - <ul> for third-level items. Defaults to 'dropdown-menu'.
nested_dropdown_item_classes - Links at third level. Defaults to 'dropdown-item'.
navbar_toggler_classes - Toggler button classes. Defaults to 'navbar-toggler'.
navbar_toggler_aria_label - aria-label on toggler. Defaults to 'Toggle navigation'.
collapse_classes - Collapse wrapper classes. Defaults to 'collapse navbar-collapse'.
trailing_markup - Optional markup after the root <ul> (still inside collapse when toggler is true).
{% endcomment -%}
{%- if menu.is_valid -%}
{%- var max_menu_depth = max_menu_depth | default: 2 | to_int | at_least: 2 | at_most: 3 -%}
{%- var show_navbar_toggler = show_navbar_toggler | default: true -%}
{%- var nav_link_classes = nav_link_classes | default: 'nav-link' | escape -%}
{%- var nav_link_classes_mobile = nav_link_classes_mobile | default: 'nav-link' | escape -%}
{%- var dropdown_toggle_classes = dropdown_toggle_classes | default: 'nav-link dropdown-toggle' | escape -%}
{%- var dropdown_menu_classes = dropdown_menu_classes | default: 'dropdown-menu' | escape -%}
{%- var dropdown_item_classes = dropdown_item_classes | default: 'dropdown-item' | escape -%}
{%- var nav_item_leaf_mobile_classes = nav_item_leaf_mobile_classes | default: 'nav-item d-lg-none' | escape -%}
{%- var nav_item_leaf_desktop_classes = nav_item_leaf_desktop_classes | default: 'nav-item d-none d-lg-block' | escape -%}
{%- var nav_item_dropdown_classes = nav_item_dropdown_classes | default: 'nav-item dropdown' | escape -%}
{%- var nested_submenu_li_classes = nested_submenu_li_classes | default: 'dropend' | escape -%}
{%- var nested_dropdown_toggle_classes = nested_dropdown_toggle_classes | default: 'dropdown-item dropdown-toggle' | escape -%}
{%- var nested_dropdown_menu_classes = nested_dropdown_menu_classes | default: 'dropdown-menu' | escape -%}
{%- var nested_dropdown_item_classes = nested_dropdown_item_classes | default: 'dropdown-item' | escape -%}
<nav class="{{ nav_classes | default: 'navbar navbar-expand-lg' | escape }}"{% if nav_id is_valid %} id="{{ nav_id }}"{% endif %} aria-label="{{ aria_label | default: 'Main' | escape }}">
<div class="{{ container_classes | default: 'container' | escape }}">
{%- if show_navbar_toggler -%}
{%- id collapse_id = suffix: '_collapse' -%}
<span class="{{ mobile_menu_label_classes | default: 'navbar-text d-lg-none' | escape }}">{{ mobile_menu_label | default: 'Menu' }}</span>
<button class="{{ navbar_toggler_classes | default: 'navbar-toggler' | escape }}" type="button" data-bs-toggle="collapse" data-bs-target="#{{ collapse_id }}" aria-controls="{{ collapse_id }}" aria-expanded="false" aria-label="{{ navbar_toggler_aria_label | default: 'Toggle navigation' | escape }}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="{{ collapse_classes | default: 'collapse navbar-collapse' | escape }}" id="{{ collapse_id }}">
{%- endif -%}
<ul class="{{ ul_classes | default: 'navbar-nav' | escape }}{% unless show_navbar_toggler %} w-100{% endunless %}">
{%- for menu_item in menu -%}
{%- if menu_item.items.size > 0 -%}
<li class="{{ nav_item_dropdown_classes }}">
<a class="{{ dropdown_toggle_classes }}" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
{{- menu_item.name -}}
</a>
<ul class="{{ dropdown_menu_classes }}">
{%- for sub in menu_item.items -%}
{%- if max_menu_depth >= 3 and sub.items.size > 0 -%}
<li class="{{ nested_submenu_li_classes }}">
<a class="{{ nested_dropdown_toggle_classes }}" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">{{ sub.name }}</a>
<ul class="{{ nested_dropdown_menu_classes }}">
{%- for grand in sub.items -%}
<li><a class="{{ nested_dropdown_item_classes }}" href="{{ grand.href.value }}">{{ grand.name }}</a></li>
{%- endfor -%}
</ul>
</li>
{%- else -%}
<li><a class="{{ dropdown_item_classes }}" href="{{ sub.href.value }}">{{ sub.name }}</a></li>
{%- endif -%}
{%- endfor -%}
</ul>
</li>
{%- elsif show_navbar_toggler -%}
<li class="{{ nav_item_leaf_mobile_classes }}">
<a class="{{ nav_link_classes_mobile }}" href="{{ menu_item.href.value }}">{{ menu_item.name }}</a>
</li>
<li class="{{ nav_item_leaf_desktop_classes }}">
<a class="{{ nav_link_classes }}" href="{{ menu_item.href.value }}">{{ menu_item.name }}</a>
</li>
{%- else -%}
<li class="nav-item">
<a class="{{ nav_link_classes }}" href="{{ menu_item.href.value }}">{{ menu_item.name }}</a>
</li>
{%- endif -%}
{%- endfor -%}
</ul>
{%- if trailing_markup is_valid -%}
{{ trailing_markup }}
{%- endif -%}
{%- if show_navbar_toggler -%}
</div>
{%- endif -%}
</div>
</nav>
{%- endif -%}