Changelog

4.0.1 2026-09-23

Changed

  • Render author-entered node templates in proper Twig sandbox.

Fixed

  • Fixed non-admin users being unable to reorder menu nodes adjacent to nodes they cannot otherwise author (#458 (opens new window)).
  • Fixed applying new sites and their menu settings from project config in read-only environments.
  • Fixed saving one settings page resetting settings on the other pages.
  • Fixed control panel redirect loops when Craft routes requests through its configured path query parameter (#457 (opens new window)).
  • Fixed Navigation 3 node-type visibility conditions losing their selected value after upgrading to Navigation 4 (#456 (opens new window)).

4.0.0 2026-09-22

{warning} Before upgrading to Navigation 4, review the Upgrading from v3 guide for breaking changes and required migration steps.

Breaking Changes

  • Menu builder changes are now staged until the menu is saved. Editors must save the menu to publish node additions, deletions, and structure changes.
  • craft.navigation.getActiveNode() now returns the deepest exact current-page match instead of the first active ancestor.
  • Custom URLs no longer expand environment variables or Craft aliases. Use relative URLs, literal destinations, or the {site.baseUrl} token.

Added

  • Added a React control panel menu builder with staged build sessions for structure moves, node additions and deletions, and menu content drafts, including publish and discard workflows.
  • Added a Menu Craft element with menu-level custom fields, public menu queries, and dedicated Menu Fields and Menu Content editing interfaces.
  • Added explicit node type classes and extensible schemas and editors for node creation and editing (#413 (opens new window)).
  • Added the Group/column structural node type (#416 (opens new window)) and Node::getTag() support for passive and structural nodes (#369 (opens new window)).
  • Added a unified Dynamic node type with extensible entry, category, asset, and Commerce product sources; read-time projected children; source conditions and ordering; optional limits; and pending/preview projections (#413 (opens new window)).
  • Added per-site node link settings for URLs, suffixes, and linked elements (#405 (opens new window), #360 (opens new window)).
  • Added menu title translation settings so node title overrides can propagate by site, site group, language, or custom key (#394 (opens new window)).
  • Added controls for whether propagated nodes are enabled on other sites by default (#400 (opens new window)), an auto-enable-new-sites plugin setting (#428 (opens new window)), and Feed Me mapping for per-site linked elements (#405 (opens new window)).
  • Added node copying between sites with per-copy element remapping (#425 (opens new window), #453 (opens new window)).
  • Added migration support for MenuBuilder, Navkit (opens new window), and FreeNav menus, including compatible hierarchy, site, element-link, attribute, and dynamic-source data.
  • Added the Context API through craft.navigation.context(handle), including sibling, branch, and ancestor helpers (Discussion #368 (opens new window)).
  • Added menu breadcrumbs through craft.navigation.menuBreadcrumbs(handle) (#335 (opens new window)) and URL-segment breadcrumbs through craft.navigation.urlBreadcrumbs().
  • Added batched linked-element hydration, in-memory hierarchy wiring, and tree assembly through the NodeRead service (#341 (opens new window), #412 (opens new window)).
  • Added tagged front-end navigation caching with configurable modes and profiles, plus craft.navigation.invalidateCache() and NavigationCache::EVENT_INVALIDATE for custom cache integrations (#367 (opens new window)).
  • Added node query controls for menu, linked-element, hierarchy, projected-child, and navigation-cache hydration, including automatic hierarchy wiring for navigation-scoped reads (#341 (opens new window), #413 (opens new window)).
  • Added single-pass active-state resolution, craft.navigation.getActiveNodes(), craft.navigation.getCurrentNodes(), and current and hasActiveChild tree output (#418 (opens new window)).
  • Added GraphQL menu, navigation context, and menu breadcrumb queries (#325 (opens new window), #335 (opens new window), Discussion #368 (opens new window)), plus node hydration arguments and projected-node schema support (#413 (opens new window), #422 (opens new window)).
  • Added an indicator when an element-linked node has a custom title and no longer follows its linked element’s title.
  • Added Pest integration and coverage for build sessions, builder state, menu content, Dynamic node schemas, linked-element lifecycles, and active-state behavior (#413 (opens new window)).
  • Added documentation for node query cache and hydration controls, active-state helpers, GraphQL site and language behavior, linked-element lifecycles, and cache invalidation (#426 (opens new window)).

Changed

  • Navigation now requires Craft CMS 5.9.11 or later.
  • Renamed the Navs service and related APIs, database tables and columns, project config paths, permissions, GraphQL schema components, control panel routes, templates, events, and internal settings from “nav” to “menu”; deprecated compatibility shims and redirects remain where documented.
  • Node type values and menu permission settings now use Navigation node type class names and are migrated automatically.
  • Dynamic nodes are removed when their source section, category group, volume, or product type is deleted (#385 (opens new window)).
  • Linked-element lifecycle handling now uses node type classes and per-site linked elements (#386 (opens new window)); soft deletion disables nodes while preserving their previous state, restoration re-enables them, and hard deletion removes them.
  • Linked entry title synchronization now respects per-site node title overrides (#230 (opens new window), #394 (opens new window)).
  • Active-state matching now includes per-site URL suffixes and skips control panel, console, and preview requests (#360 (opens new window), #384 (opens new window)).
  • Improved active-state matching for entry-backed nodes on sites with path-based URLs (#408 (opens new window)), site nodes on descendant pages (#435 (opens new window)), and duplicate URL matches by returning the deepest current-page match (#418 (opens new window)).
  • Improved node.hasActiveChild(), craft.navigation.render(), and craft.navigation.tree() to use resolved in-memory hierarchy and active-state data instead of recursive structure queries.
  • Improved cache invalidation for menu and node changes; active and ancestor state is applied after cache hits rather than stored in cached payloads.
  • Dynamic projections now default to Craft’s live/public statuses, with non-live sources available only through explicit pending or preview reads.
  • Author-supplied URL, class, and custom-attribute tokens now render in Craft’s sandboxed Twig environment with bounded site context. Custom URLs no longer expand environment variables or aliases; use a relative URL, literal destination, or the sandboxed {site.baseUrl} token.
  • Passive and group nodes can render as <span> rather than <a> through getTag(); custom templates that assume every node is an anchor must be updated (#369 (opens new window)).
  • Restructured the plugin documentation into Menus, Templates, Frontend, GraphQL, Reference, and Integrations sections.

Fixed

Deprecated

  • Deprecated craft.navigation.nav(), getNavByHandle(), getNavById(), getAllNavs(), breadcrumbs(), and getRegisteredElements() in favor of the corresponding menu, URL breadcrumb, and node type APIs.
  • Deprecated Node::getNav(), getIsActive(), and setIsActive() in favor of getMenu(), getActive(), and getActiveState().
  • Deprecated Menu::getNav() in favor of the Menu element API or getMenuHandle().
  • Deprecated NodeQuery::nav(), navHandle(), navId(), and elementSiteId() in favor of menu(), menuHandle() / handle(), menuId(), and per-site link settings.
  • Deprecated the navHandle, nav, and navId node query criteria keys in favor of handle and menuId.
  • Deprecated the nav, navHandle, and navId GraphQL node query arguments in favor of menuHandle and menuId, and the navId, navHandle, and navName fields in favor of their menu* equivalents.
  • Deprecated the Navs plugin API, PluginTrait::getNavs(), NavEvent, EVENT_*_NAV events, and $event->nav in favor of their Menu equivalents.
  • Deprecated the Elements service and RegisterElementEvent in favor of NodeTypes and RegisterNodeTypeEvent with ElementNodeType subclasses.
  • Deprecated the builderLiveStructure plugin setting; structure changes are staged through build sessions by default.

Removed

  • Removed the disabledElements and propagateSiteElements plugin settings.
  • Removed the legacy _types/*/modal.html Twig templates for Site, Custom URL, and Dynamic nodes; slide-out fields now render through PHP helpers (#413 (opens new window)).