Version 5.1.2
August 13, 2026
Fixed
- Fixed the add/edit node panel closing when you dismissed a Craft element selector opened from inside it. The panel hand-rolled its own stacking and dismissal: its overlay sat above Craft's modal shade and swallowed the click, and its Escape handler was bound to the document, so both fired for interactions that belonged to the modal.
- Fixed Craft's element selector rendering behind the panel, which covered the modal's Select button. Panels now sit on Craft's z-index for modals and slideouts and order themselves by position in
<body>, so a modal opened from a panel stacks above it. - Panels now register as Garnish UI layers, so Escape closes the topmost thing — the element selector first, then the panel — the way the rest of the control panel behaves.
Version 5.1.1
August 13, 2026
Fixed
- Fixed the node builder always operating on the primary site, whatever the site selector said. Craft doesn't apply
?site=to the current site on control panel requests —Cp::requestedSite()does — so on a menu that isn't enabled for the primary site every save failed with "Attempting to save an element in an unsupported site", and menus whose nodes live on another site showed up empty. - The builder now redirects to a site the menu is actually enabled for, and shows a site selector when a menu is enabled for more than one.
- Fixed
propagationMethoddoing nothing. It was stored, validated, exposed in the CP, exported and migrated, butNode::getSupportedSites()never read it, so nodes propagated to every enabled site no matter the setting. All four methods (none,siteGroup,language,all) now apply. See Changed below. - Fixed management operations silently doing nothing for menus whose nodes live outside the current site: JSON export wrote an empty node list,
resave-nodesand the console menu listing reported 0 nodes, the max-nodes limit went unenforced, and deleting a menu left its nodes behind as orphans. - Fixed node title syncing following the request's site instead of the saved element's, so only one site's nodes picked up a retitled element.
- Adding a node to a site its menu isn't enabled for now fails with a message naming the menu and site, instead of an uncaught exception.
Changed
- Menus set to a propagation method other than "all" will stop propagating new saves to sites the method excludes. Existing rows are left as they are — nothing is deleted — so a menu that had been propagating everywhere keeps the nodes it already has.
Nodes::getNodesByMenuId()andNodes::getParentOptions()take an optional$siteId.
Added
Nodes::findNodesInMenu()— every node in a menu, once each, across all sites. Use it for management operations; rendering stays scoped to the current site.Menu::getEnabledSiteIds()andMenu::isEnabledForSite().
Version 5.1.0
August 13, 2026
Fixed
- Fixed nested menus rendering flat through
craft.freenav.tree(). Node hierarchy lives in the menu's structure, but the tree builder grouped nodes by aparentIdcolumn that nothing ever wrote, so every node came back as a root. Trees are now built from the structure'slevel. - Fixed
Nodes::addNodes()silently flattening menus on multi-site installs. The parent node was looked up against the current site only, so a parent living on another site resolved tonulland the child was appended to the root instead. Parent lookups now search all sites and prefer the new node's site. - Fixed
node.urlin Twig and GraphQL returning the raw custom URL — empty for element-linked nodes — instead of the resolved one. Theurlproperty shadowedgetUrl(); it is nowcustomUrl, leavinggetUrl()as the only accessor. See Changed below. - Fixed duplicated menus losing their nesting:
Menus::duplicateMenu()copied nodes but never added them to the new menu's structure, so the copy came out flat (and invisible to structure-ordered queries). - Fixed nodes whose parent is hidden (disabled or failing a visibility rule) being promoted to the top level when rendering. They are now dropped along with their parent.
- Fixed reordering a node — in the builder or the element index — leaving a stale cached menu on the front end.
- Fixed the CP builder failing to find nodes that don't exist on the current CP site.
Nodes::moveNode()no longer throws when the requested parent or sibling can't be resolved; the node falls back to the menu root and a warning is logged.
Changed
- Breaking:
Node::$urlis nowNode::$customUrl, and thefreenav_nodes.urlcolumn is nowcustomUrl. Templates should callnode.getUrl()(they always should have —node.urlreturned the wrong value for element-linked nodes). GraphQL'surlfield is unchanged and now resolves correctly. Menu exports emitcustomUrland are stamped"freeNav": "1.1.0"; imports still accept the oldurlkey. - Breaking: the unused
freenav_nodes.parentIdcolumn has been dropped, along withNode::$parentId, which shadowed Craft's ownElement::getParentId().node.parentId,node.getParent(), andnode.getChildren()now all read from the menu's structure. - Node queries now join structure data even when the query isn't scoped to a single menu, so
level,lft, andrgtare populated on anyNode::find()result.
Added
Nodes::findNodeInMenu(),Nodes::placeNode(), andNodes::moveNode()— one place that resolves and positions nodes within a menu's structure.NodeHelper::buildParentMap()— node ID => parent ID map derived from structure order.
Version 5.0.2
July 19, 2026
Fixed
- Fixed the element node types (Entry, Category, Asset, Product) being impossible to create in the node builder: the "Select Element" field now renders Craft's element picker so you can choose which element the node links to. Previously the field row appeared but its container stayed empty, and the selected element was never sent to the server. (#reported)
Added
nodes/element-select-htmlcontroller action that renders the element selector for a given linkable node type (used by the builder UI).
Version 5.0.1
July 19, 2026
Fixed
- Fixed the element index columns for nodes: the
Node TypeandNew Windowcolumns now render their styled/custom HTML again (the Craft 3/4tableAttributeHtml()hook was renamed toattributeHtml()in Craft 5, so the custom rendering was silently ignored). - Corrected the return type of
Node::getLinkedElement()and its backing property toElementInterface, matching what Craft's element lookup returns.
Dev
- Added static analysis (PHPStan level 5, clean), code-style enforcement (ECS with Craft's rule set), and a PHPUnit unit-test suite for the plugin's enums and visibility rules.
- Removed unused imports and redundant code flagged by the new tooling.
Version 5.0.0
February 15, 2026
Added
- Initial release
- Full-featured navigation menu builder for Craft CMS 5
- "Menus" terminology for clear naming
- Drag-and-drop node builder in the Control Panel
- Support for entry, category, asset, product, custom URL, passive, and site node types
- Conditional visibility rules (user group, logged-in state, URL segments, entry type)
- Built-in per-menu cache with automatic invalidation via tagged dependencies
- Icon class and badge text fields on nodes
- 6 template render presets: default, dropdown, sidebar, breadcrumb, footer, mega menu
- JSON import/export of full menu structures
- Built-in ARIA accessibility attributes (aria-current, aria-expanded, aria-haspopup, role)
- REST API endpoints for headless/decoupled architectures
- GraphQL schema with per-menu types and scoped permissions
- Mega menu column layout support
- Breadcrumb generation from URL segments with element resolution
- Multi-site support with configurable propagation methods
- Project Config support for menu definitions
- Granular user permissions (manage menus, edit nodes, delete nodes)
- MenuField field type for selecting menus in entries
- Element syncing (title/URL updates when linked elements change)
- Console commands for resaving nodes and clearing caches
- Full English translation file