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
MenuCraft 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 throughcraft.navigation.urlBreadcrumbs(). - Added batched linked-element hydration, in-memory hierarchy wiring, and tree assembly through the
NodeReadservice (#341 (opens new window), #412 (opens new window)). - Added tagged front-end navigation caching with configurable modes and profiles, plus
craft.navigation.invalidateCache()andNavigationCache::EVENT_INVALIDATEfor 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(), andcurrentandhasActiveChildtree 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
Navsservice 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
typevalues 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(), andcraft.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>throughgetTag(); 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
- Fixed cross-site node copies losing their hierarchy or returning empty
node.children, including cached level-scoped queries (#452 (opens new window)). - Fixed an error when restoring a soft-deleted menu through project config (#415 (opens new window)).
- Fixed N+1 queries when reading
node.childrenwithout pre-wired hierarchy data (#341 (opens new window)), readingnode.elementwithout linked-element hydration, and propagating ancestor active states (#412 (opens new window)). - Fixed per-site custom URLs and link settings being stored against shared node data instead of the corresponding site (#405 (opens new window), #360 (opens new window)).
Deprecated
- Deprecated
craft.navigation.nav(),getNavByHandle(),getNavById(),getAllNavs(),breadcrumbs(), andgetRegisteredElements()in favor of the corresponding menu, URL breadcrumb, and node type APIs. - Deprecated
Node::getNav(),getIsActive(), andsetIsActive()in favor ofgetMenu(),getActive(), andgetActiveState(). - Deprecated
Menu::getNav()in favor of the Menu element API orgetMenuHandle(). - Deprecated
NodeQuery::nav(),navHandle(),navId(), andelementSiteId()in favor ofmenu(),menuHandle()/handle(),menuId(), and per-site link settings. - Deprecated the
navHandle,nav, andnavIdnode query criteria keys in favor ofhandleandmenuId. - Deprecated the
nav,navHandle, andnavIdGraphQL node query arguments in favor ofmenuHandleandmenuId, and thenavId,navHandle, andnavNamefields in favor of theirmenu*equivalents. - Deprecated the
Navsplugin API,PluginTrait::getNavs(),NavEvent,EVENT_*_NAVevents, and$event->navin favor of theirMenuequivalents. - Deprecated the
Elementsservice andRegisterElementEventin favor ofNodeTypesandRegisterNodeTypeEventwithElementNodeTypesubclasses. - Deprecated the
builderLiveStructureplugin setting; structure changes are staged through build sessions by default.
Removed
- Removed the
disabledElementsandpropagateSiteElementsplugin settings. - Removed the legacy
_types/*/modal.htmlTwig templates for Site, Custom URL, and Dynamic nodes; slide-out fields now render through PHP helpers (#413 (opens new window)).