A menu in Leed is a named, ordered tree of items stored in your workspace. On its own it does nothing at all — it is not attached to a page, a template or a URL until something reaches out and asks for it. There are exactly two things that can: a template that names the menu, and a documentation page type that binds the menu to one of its three navigation slots.
That second route is where the surprise lives. A menu bound to a documentation set’s Left slot is not just navigation. Its folder names become the URL segments of every page inside it, which means saving that menu can move pages.
You edit menus in the Design workspace. The Menus section holds your site menus — headers, footers, anything a template places. The Documentation section holds the menus bound to documentation and API sets. The two lists are a strict partition: a menu appears in exactly one of them, and which one is decided by the rule in Only the Left slot makes a menu a docs menu. The panel itself, its search box and its sort control are described in Design Workspace.
Menus are free on every plan. The one gated thing that touches them is per-item menu click analytics, which is Growth and up and is documented with the rest of the reporting it feeds.
What a menu is made of
A menu has a name, which must be unique in your workspace, an Expanded flag, and an ordered list of items. Each item is either a folder or a link:
- A folder is an item that has children. In a documentation menu a folder can never also be a link — Leed blanks a folder’s link on every save. In a site menu a folder may keep a link if your template renders one.
- A link item points either at one of your pages, stored as a reference to the page rather than its URL, or at a literal address — an external site, a file in your asset library, or a hand-typed path.
Every item carries a visible name and a separate Tooltip. The name is what a reader sees in the rendered navigation. The tooltip becomes the HTML title attribute, the text a browser shows on hover. They are not interchangeable, and the CMS tree makes this easy to get wrong — it displays the tooltip when one is set and falls back to the name when one is not, so an item can look correct in the CMS while publishing something else entirely. That trap, and the rename that fixes it, are covered in Building and Editing a Menu.
The two menu families
| Family | How it is bound | Where it renders | Inherits company defaults? | Can it be deleted in the CMS? |
|---|---|---|---|---|
| Site menu | A template asks for it by name | Wherever your layout places it — header, footer, a landing page, anywhere | Not applicable — a template names it explicitly | Yes |
| Documentation menu | A documentation or API page type binds it by id to one of three slots: Left (the sidebar), Top (the docs header) or Bottom (the docs footer) | The documentation set’s own chrome, with no template work | Top and Bottom inherit your company defaults; Left never does | No — the Delete control is not offered on a menu bound as a set’s Left menu |
Site menus
A site menu reaches a page only because a template asked for it. The partial is leed/menu/builder, and it takes the menu’s name — the same name you typed when you created it:
{{> leed/menu/builder menuName="header" menuClass="site-header__nav" expanded=true }}So which menu appears in your header is a decision in your layout, not a setting in the CMS. Rename the menu in the CMS without changing the template and the header silently empties: menuName no longer resolves, the partial renders nothing, and there is no build error. The partial’s parameters and the markup it emits are documented in Menu Partials, which also covers item and link, the two partials it recurses through. If you write navigation markup by hand rather than calling the builder, three helpers exist so it can never render a link to a page that is not there — Menu-Safety Helpers.
Documentation menus
A documentation or API page type binds menus directly, through its documentation configuration. No template work is involved: the Left menu renders as the set’s sidebar, Top as the docs header row, and Bottom as the docs footer.
Creating a documentation or API page type creates a left menu for you. The backend makes the menu first, names it after the page type, and binds it — all before the page type itself is saved. So a new documentation set arrives with an empty sidebar already wired up, and you fill it in rather than creating anything.
The Top and Bottom slots inherit whatever you have set as a company-wide default; the Left slot never inherits, because a left menu is the specific spine of one specific set and sharing one across two sets is not a thing you can mean. Setting these slots, along with the rest of the configuration block, is covered in Configuring a Page Type. If you are building a documentation set rather than maintaining one, the left menu is the spine of the whole exercise and Left Navigation Menu approaches it from that end.
The three slots hold a menu id, not a menu name
This is the single most likely misconfiguration in the whole documentation stack, and it fails without a word.
The three slots store the menu’s id — an eight-character string like docs-nav. The site’s published menu file, on the other hand, is keyed by menu name. The build translates between the two by scanning the file for the entry whose stored id matches the one in your configuration, and using that entry’s key.
An id that matches nothing renders nothing at all. No build error, no warning, no fallback. Just an empty sidebar, or a docs header with no links in it.
Only the Left slot makes a menu a docs menu
Leed decides whether a menu is a “docs menu” by one test, and one test only: is this menu id the Left slot of some documentation or API page type? Every rule in the next section is gated on that answer.
The consequence is more useful than it sounds. A menu you reuse in the Top or Bottom slot is not reclassified. Its folders keep their links. Its page items may have children. No page path is derived from it, and moving something in it moves nothing on disk. It stays an ordinary site menu that happens to also render above and below your documentation.
That is exactly how you put your existing site header and footer around a documentation set: point Top at the same menu your marketing header uses and Bottom at your footer menu, and the two surfaces share one navigation with no second copy to keep in step. Note one asymmetry while you are there — the docs header’s call-to-action button is a single object, not a list, so a documentation set gets one header button and not a row of them. The full treatment of reusing your chrome is Documentation Header, Footer and Logos.
The same test also decides which list a menu shows up in inside the Design workspace. Bind a menu as some set’s Left menu and it moves from Menus to Documentation.
The rules that only apply to a docs menu
These are enforced on the server, when the menu is saved, and they are the source of nearly every menu error message you will see. All of them follow the Left binding only.
| Rule | What you cannot do | What you get | Applies in the Top or Bottom slot too? |
|---|---|---|---|
| A folder is never a link | Give a folder its own destination | No error — the link is silently blanked on every save | No |
| A page item cannot have children | Nest anything under a page | 400 — “A docs page item cannot have children — place pages inside folders instead” | No |
| A page appears once, in one docs menu, workspace-wide | Add the same page twice, or add a page that already sits in another set’s left menu | 409 — “Duplicate page in menu: the page is already in this menu, so the add was rejected — a page may only be referenced once, in a single docs menu”, or “A docs page may only be referenced once, in a single docs menu” | No |
| Navigation Menu Locked | Reorder, move, rename a folder, or remove a page from a locked set | 403 — “Cannot change navigation: the ‘ | No |
| A page may not sit exactly on another set’s root | Move a page so its composed URL is exactly another page type’s root path | 409 — “Cannot move page: the path ‘…’ is the root path of the ‘ | No |
| A path can only belong to one page | Move a page into a folder where the composed URL is already taken | 409 — “Cannot move page: the path ‘…’ is already in use by another page” | No |
The single-placement rule is the one people push back on, because a page genuinely can be relevant in two places. The answer is not a second menu entry. Link to it from the prose of the neighboring page, which costs nothing and reads better, or give it a second URL with an alias as described in Aliases and Redirects.
A docs menu is also your URL tree
A documentation page’s URL is its page type’s slug, then one segment per folder it sits inside, then its own slug:
/{page-type-slug}/{folder}/{folder}/{page-slug}/Each folder segment is that folder item’s name, slugified. Not its tooltip. Not a field on the page. The left menu is the only thing in Leed that sets those middle segments, and three things follow from that:
- No frontmatter key sets a page’s URL. The folder path is not written into the page’s published frontmatter at all — it is composed from the menu at build time.
- A path supplied when a page is created is ignored, by design, for a page type whose docs menu is bound; and the update endpoint does not accept a path at all. The comment in the code says why: a path written straight to the page record would not stage a matching URL row, so the CMS and the published site would disagree about where the page lives.
- Renaming or moving a folder moves every page beneath it. Not eventually — the next save of that menu stages a new URL for each affected page, and if you hold publish rights it commits and deploys the moved pages then and there.
The composition rule itself — where each segment comes from, how slugs are derived, and what happens when two would collide — is drawn once, on How a URL is composed.
What a reader actually gets
The published navigation is not a literal rendering of your tree. Items are filtered as the site builds, and the filter is quiet.
| Item shape | What the reader sees |
|---|---|
| A folder | Rendered. Its label sits in a plain <div>, not a link, so the children stay reachable |
| A link item pointing at a published page | Rendered as a real <a> |
| A link item pointing at an unpublished or deleted page | The whole item is omitted — no label, no placeholder, nothing |
| A folder whose own link points at an unpublished page | Still rendered; the label is not a link, and the children are untouched |
| Any item marked Disabled | Omitted, whatever else is true about it |
| A link item pointing at an external address or a file | Rendered, with a small marker after the label when it opens in a new tab |
The third row is the one to plan around. You can build a whole navigation ahead of a launch — the link picker offers unpublished pages precisely so you can — but you cannot check it until the pages are published, because until then the entries simply are not there. Publish the pages first, then look at the nav.
Previous and next links on a documentation page are filtered differently and can leave a page’s title rendered as plain, unlinked text; Linking Between Docs Pages covers that case.
How a menu reaches the site
flowchart TD
M[("The menu record<br/>in your workspace")]
M --> PUB["Publish, reason: menus<br/>merged into _data/menu.json<br/>under the menu NAME"]
PUB --> SITE{"How is it<br/>referenced?"}
SITE -->|"A template names it<br/>(by NAME)"| T["The builder partial<br/>looks the name up"]
T --> OUT1["Header, footer, or wherever<br/>your layout places it"]
SITE -->|"A docs page type<br/>binds it (by ID)"| SLOT["The page type's<br/>menu slots"]
SLOT --> TOPB["Top / Bottom"]
SLOT --> LEFT["Left"]
TOPB --> OUT2["The docs header row<br/>and the docs footer"]
LEFT --> SB["The documentation sidebar"]
LEFT --> URL["Folder names, slugified,<br/>become the middle segments<br/>of every page's URL"]
Menus publish through the same Deploy flow as everything else, under the reason menus. Three details are worth knowing:
- Only dirty menus are included. A menu you have not touched since its last publish is skipped, even when you select it.
- The Deploy selection is the menu, not its pages. Publishing a menu does not publish the pages it points at, which is why an unpublished target vanishes from the rendered nav rather than appearing broken.
- The site file is merged by menu name, and keys are never removed. Publishing writes your menu into
src/_data/menu.jsonunder its name, leaving every other key alone.
The Deploy tab and how to publish a subset of pending changes are covered in Publishing Changes.
Never rename a published menu
Pick the menu’s name when you create it, and treat it as permanent from the first publish. If you truly must change it, create a new menu under the new name, rebuild the tree, update every template that names the old one, and delete the old menu so its key is removed.
Once a menu is live, every rendered link carries a data-reason attribute naming the menu it came from and the item that was clicked — the channel per-item click tracking rides on, described in Journeys, Funnels and Attribution.