You are viewing beta documentation for Navigation 4.x.
Templates

Rendering Nodes

Outputting a menu usually starts in one of two ways: let Navigation render a nested list for you, or query nodes and build your own markup.

craft.navigation.render()

The quickest option — Navigation outputs a nested <ul> with active classes applied.

{{ craft.navigation.render('mainMenu', {
    ulClass: 'nav-items',
    liClass: 'nav-item',
    aClass: 'nav-link',
    activeClass: 'nav-active',
    currentClass: 'nav-current',
    hasChildrenClass: 'nav-has-children',
}) }}

render() outputs a fixed nested list from stored node titles and URLs. Parent/child relationships are wired in memory on front-end requests — you do not pass hydration or hierarchy flags for typical use.

craft.navigation.nodes()

Use a node query when you need full control over HTML, mega-menu layouts, node custom fields, or linked element data (withLinkedElements()).

{% set nodes = craft.navigation.nodes()
    .handle('mainMenu')
    .all() %}

<ul>
    {% nav node in nodes %}
        <li>
            {{ node.link }}

            {% ifchildren %}
                <ul>{% children %}</ul>
            {% endifchildren %}
        </li>
    {% endnav %}
</ul>

You can pass the menu handle as a shorthand:

{% set nodes = craft.navigation.nodes('mainMenu').all() %}

On front-end menu-scoped reads, parent/child relationships are wired in memory automatically — you do not need Craft eager-loading for hierarchy in most cases. See Performance & Caching.

For query parameters, see Node Queries.

Recursive macro (without {% nav %})

If you prefer macros, fetch only top-level nodes and recurse manually:

{% import _self as macros %}

{% set nodes = craft.navigation.nodes()
    .handle('mainMenu')
    .level(1)
    .all() %}

<ul>
    {% for node in nodes %}
        {{ macros.navigationNodes(node) }}
    {% endfor %}
</ul>

{% macro navigationNodes(node) %}
    {% import _self as macros %}

    <li>
        {{ node.link }}

        {% if node.children %}
            <ul>
                {% for subnode in node.children %}
                    {{ macros.navigationNodes(subnode) }}
                {% endfor %}
            </ul>
        {% endif %}
    </li>
{% endmacro %}

Custom markup

When building links yourself, use Node helpers and active-state methods:

{% set nodes = craft.navigation.nodes('mainMenu').all() %}

<ul>
    {% nav node in nodes %}
        <li class="{{ node.getActive() ? 'is-active' }}{{ node.getCurrent() ? ' is-current' }}">
            <a {{ node.linkAttributes({
                class: node.classes,
            }) }}>
                {{- node.title -}}
            </a>

            {% ifchildren %}
                <ul>{% children %}</ul>
            {% endifchildren %}
        </li>
    {% endnav %}
</ul>

Passive and group nodes may not use <a> tags — see Custom Rendering.