You are viewing beta documentation for Navigation 4.x.
Template Guides

Active State & Context

Highlight the page a visitor is viewing and its menu branch using each node’s current and active states. For example, on a product page you can mark the product link as current while keeping the Products section highlighted. The examples use a saved mainMenu and belong in the Twig partial that displays it.

Active state is always computed after cache hits and is skipped in the CP, console, and preview requests.

States

StateMethodTypical use
Currentnode.getCurrent()aria-current="page", primary highlight
Activenode.getActive()Open branch / .active on parent
Has active childnode.hasActiveChild()Open submenu, chevron icon

Current is an exact URL match (including urlSuffix). Active is true when the node is current, when any descendant nav node is current, or when path-section matching applies. Has active child is true when a descendant nav node — not just a URL under the parent path — is current.

Path-section matching covers URLs that sit under a node’s path when no child nav item exists for that page. The /news node is active on /news/article-slug even if the article is not in the menu.

<li class="{{ node.getActive() ? 'is-active' }}{{ node.getCurrent() ? ' is-current' }}">

Find the Current Page

{# Deepest exact-match (current page) node #}
{% set currentNode = craft.navigation.getActiveNode({ handle: 'mainMenu' }) %}

{# Branch fallback when no exact node exists for the URL #}
{% set sectionNode = craft.navigation.getActiveNode({ handle: 'mainMenu' }, true) %}

{# All exact matches, deepest first #}
{% set currentNodes = craft.navigation.getCurrentNodes({ handle: 'mainMenu' }) %}

{# All branch-active nodes #}
{% set activeNodes = craft.navigation.getActiveNodes({ handle: 'mainMenu' }) %}

Default Behaviour

getActiveNode() (default) returns the deepest node whose URL exactly matches the current page.

On /products/widget with nodes for /products and /products/widget, getActiveNode() returns Widget. Both Products (getActive()) and Widget (getCurrent()) are highlighted appropriately.

Pass true as the second argument when you need branch/path fallback for contextual sub-nav.

Context API

For sidebars and section navigation, use craft.navigation.context() to find related pages without separate parent and sibling queries:

{% set ctx = craft.navigation.context('mainMenu') %}

<ul>
    {% for node in ctx.siblings() %}
        <li class="{{ node.getCurrent() ? 'is-current' }}">
            <a href="{{ node.url }}">{{ node.title }}</a>
        </li>
    {% endfor %}
</ul>

Use siblings() for pages beside the current page, or children() when the current page is a section root. branch() combines the current node, its ancestors, and siblings. The Context reference lists all methods and empty-match behaviour.

See Context & Breadcrumbs for breadcrumb trails.

Dynamic Children

Projected entries from Dynamic nodes participate in active resolution. getActiveNode() and getCurrentNodes() can return a ProjectedNode when the current URL matches a projected entry.

Override Active State

Branch active state can be modified via Node::EVENT_NODE_ACTIVE when getActive() runs. See Events.