Documentation is English-only for now. The rest of the site follows your language.
Menus
Admin → Menus is a visual designer: a drag-and-drop item tree, layout and mega-menu presets, per-item styling, visibility rules, and a live preview. The primary menu cannot be deleted.
7 min read
Admin → Menus is a full designer, not a flat link list. CRUD is at /api/menus for administrators and editors. Items can be a custom URL or any CMS content type — pages, posts, products, shop pages, and custom types — and they nest. Saving stores the content-type slug; public menus resolve those links like pages. The primary menu is undeletable.
Public rendering reads menus through jf-cache (menus: prefix) and the `navigation.items` filter, which now runs *after* the host resolves a menu (visibility rules already applied), so appended items sit alongside the author's.
The designer
- A drag-and-drop item tree with indent / outdent and undo / redo.
- A live `?preview=1` iframe of the rendered menu.
- One-click design presets — a layout plus activation, breakpoint, and alignment an author picks instead of configuring from scratch.
- Edits autosave to a
draft_items/draft_designworking copy that only the preview path reads; Publish promotes it and clears the draft.
Layout and design contract
Each menu carries a layout / design contract in a design column (migration 0024_menu_designer). A NULL design renders the built-in defaults, so every existing menu is unchanged.
- Layout —
horizontal,vertical,dropdown,multi-level-dropdown,mega,footer,drawer. - Activation — hover or click.
- Alignment and a per-menu mobile breakpoint with a collapse pattern (
dropdown,accordion,drawer-right,drawer-left,fullscreen) and enter / exit motion. - Depth and items-per-level caps.
Per-item options
A menu item gains a style preset and per-button styling (background / text / border colours validated as safe CSS values, radius, size, full-width), an icon or image (validated as safe asset URLs), a badge, a description line, title text, and extra rel tokens (nofollow, sponsored, ugc, external — noopener noreferrer is always added for target="_blank").
A mega layout's top-level items hold multi-column regions whose content is authored as blocks, sanitized on write against a fixed safe-block allowlist (no core.html / core.code / core.embed) and rendered through the same block pipeline as page content.
Visibility rules
An item can be shown or hidden by visitor auth state, role, locale, or a plugin-provided condition. The checks are enforced server-side and fail closed — an unknown or deactivated condition hides the item. A menu that uses any auth / role / condition rule bypasses the shared public cache so every request resolves against the real session; the cacheability test itself is cached and invalidated on every menu save, so menus without rules cost nothing extra. Device targeting (desktop / tablet / mobile) is presentation-only, applied with the shared data-jf-devices CSS primitive now emitted into /theme.css for every theme.
Theme front-end
The designer ships a partials/nav-menu.ejs renderer, /js/site-nav.js (desktop flyouts, an off-canvas mobile drawer, keyboard navigation, prefers-reduced-motion), and the .jf-nav styles in the default theme's global.css.
Extending menus
Plugins and themes contribute one-click starting points with the `menu.design.presets` filter (static MenuDesignSeed data, no build(); ids are "<pluginId>:<slug>") and answer an item-level custom condition with `menu.visibility.evaluate`. Append link-shaped items with `navigation.items`. See Hooks and docs/HOOKS.md → Contributing a menu design preset in the CE repository.
ctx.hooks.filter("navigation.items", (items, { location }) => {
if (location !== "primary") return items;
return items.filter((item) => item.url !== "/hidden");
});