Folders Set Your URLs

A documentation page’s URL is its folder location. Nothing else contributes to it:

/{page type path}/{folder names, slugified}/{page slug}/

Three consequences follow, and the rest of this page will not make sense without them.

  1. No frontmatter key sets the URL. A page’s published file carries title, slug, summary, pageId and the rest — but not its path. There is nothing to edit.
  2. A path sent to the create endpoint is ignored, deliberately, for a documentation page type with a bound menu. The shipped comment gives the reason: so the URL can never contradict the page’s actual menu placement. The update endpoint does not accept path at all.
  3. The documentation left menu is therefore the only place folder placement is edited, and saving it physically moves pages.

How a path is composed in general — both lanes drawn out, with the -2 de-duplication loop — is at URL Paths and Slugs. This page is about what folders do to it.

The folder’s name is the URL segment, not its tooltip

The menu item dialog offers a folder both a name and a Tooltip, and only one of them is load-bearing:

FieldWhere you edit itWhat it does to the URL
NameDouble-click the folder in the treeSlugified into the path segment for every page beneath it
TooltipThe gear dialogNothing. It is hover text

So renaming a folder moves every page under it. Renaming its tooltip moves nothing. If you meant to relabel a section without touching URLs — and you often do — there is no way to do it: the label is the segment. Change the tooltip, or accept the move.

What slugification does to a folder name

Folder names are slugified with the same slugifier Leed uses everywhere. Ampersands become and, spaces become hyphens, punctuation is dropped, and everything is lowercased.

Folder nameURL segment
Getting Startedgetting-started
Pages & Editingpages-and-editing
AI & MCPai-and-mcp
Media & Assetsmedia-and-assets
Documentation Sitesdocumentation-sites
Leed Markdown Formatleed-markdown-format

There is no separate slug field on a folder, so choose folder names whose slug is the URL segment you want. That is not a style preference; it is the only control you have. This documentation set is the worked example: every folder in its left menu is named so that its slug is exactly the URL segment we wanted, which is why the set could be reorganized during authoring without a single redirect.

The page’s own slug

The last segment is the page’s slug, and it is derived from the title — slugified, then de-duplicated with -2, -3 and so on until the composed URL is free within your workspace. A slug supplied when a page is created is ignored; the derived one wins. This is also why an AI client or importer cannot choose a page’s slug at creation time, and it is worth knowing before you author a page that links to it.

Change a slug afterwards from the page’s own settings — and do it before anything links to the page, so no other page has to be revisited.

What a mismatch actually costs

The rule that a page’s file location equals page type path + folder path + slug is enforced in code, not by convention:

  • On the repository side, a page’s folder path is derived from the markdown file’s directory relative to src/<page type path>/. A file that disagrees with its record is a hard failure, not a warning.
  • The stored folder path is constrained to lowercase segments with no leading or trailing slash — ^[a-z0-9-]+(/[a-z0-9-]+)*$ — so a path that is not already slug-shaped cannot be written at all.
  • Composed URLs are unique per workspace, enforced by the database.

A page whose menu placement and stored path disagree therefore looks fine right up until the next documentation-menu save, when the recompute notices and stages a URL move for it. That move ripples: a new row in the paths table, a redirect from the old URL, a new sitemap entry, a break in analytics continuity for the old address, new breadcrumbs, a new position in the previous/next chain — and it can collide with a URL another page already owns, which rejects the whole menu save.

This is why the invariant is worth respecting rather than working around. It is checkable, and the check runs whether or not you were thinking about it.

Renaming or moving a folder

Every page beneath the folder gets a new URL. What happens next depends on one thing: whether you can publish.

flowchart TD
  A["Old folder ancestry"] --> C["Folder path change<br/>computed per affected page"]
  B["New folder ancestry"] --> C
  C --> D{"Can you publish<br/>this menu?"}
  D -- "yes" --> E["New path committed<br/>and deployed now"]
  D -- "no" --> F["New path staged as 'next'<br/>lands at the page's next publish"]
  E --> G["Old URL kept as a permanent redirect"]
  F --> G

The branch is the thing prose keeps burying: the same action takes effect at two different moments depending on who performed it. With publishing rights, the moved published pages are committed and deployed straight away and the new URLs are live within minutes. Without them, each page’s new URL is staged and lands the next time that page is published — so a writer can reorganize the navigation without shipping a URL change nobody reviewed. Which roles carry which rights is in Roles and Permissions.

A staged move is visible: open any affected page’s settings and you will see both its live URL and the pending one.

flowchart TD
    A["Rename a folder in the Documentation tree"] --> B{"Do you hold publishing rights?"}
    B -- Yes --> C["Published pages move immediately"]
    B -- No --> D["The move is staged"]
    D --> E["Page settings show the live URL<br/>alongside the pending one"]
    E --> F["Applied at the next publish"]

Old URLs are not lost

When a page’s URL changes, its previous URL is kept and becomes a permanent redirect. Readers with the old link, and search engines with the old address, land on the new page. You do not create the redirect and you cannot forget to. What Leed creates for you automatically, and what you can add by hand, is at Aliases and Redirects.

Why a move was rejected

A menu save that would produce an illegal URL is rejected whole — no page is moved and the menu is not saved.

CauseStatusExact messageWhat to change
The set’s navigation is locked403Cannot change navigation: the '<page type>' page type's navigation is locked. An Administrator or Content Publisher can uncheck Navigation Menu Locked in Settings → Page Types.Uncheck Navigation Menu Locked on the page type, or leave the URLs alone
A page would land exactly on another page type’s root409Cannot move page: the path '<path>' is the root path of the '<page type>' page typeRename the folder, or file the page one level deeper. Overlapping another set’s namespace is allowed — landing exactly on its root is not
The target URL is already owned by another page409Cannot move page: the path '<path>' is already in use by another pageChange this page’s slug, or rename the folder so the composed path differs

If the move is rejected because navigation is locked, the checkbox is on the page type: see Configuring a Page Type.

Two placements to avoid

Do not park a page at the set root. A page with an empty slug composes to /{page type path}/, and URLs are unique per workspace — so whatever already answers at that address, whether a landing page built from your own templates or another set’s index, collides with it. If you want a landing page for the set, give it a real slug and point the set’s Starting Page at it.

Do not give two sibling folders names that slugify the same. Pages & Editing and Pages and Editing both become pages-and-editing. Nothing warns you at the moment you type the second name; you find out when the first page you move into it collides with a URL that already exists.

A worked reorganization

Suppose Getting Started should have been Start Here.

  1. Rename the folder. Double-click it in the set’s tree, type Start Here, and save the menu. Every page beneath it now targets /docs/start-here/….
  2. Check one page. Open a page from that folder and look at its settings. If you can publish, its URL already reads /docs/start-here/…. If not, you see the live /docs/getting-started/… URL and the staged /docs/start-here/… beside it.
  3. Publish. A staged move lands when the page publishes. Publishing the whole set at once is the sane way to do it — see Publishing a Documentation Set.
  4. Verify the redirect. Once the site has rebuilt, request the old URL. It should answer with a permanent redirect to the new one. If it does not, the page has not published yet — the redirect is written from the page’s own record, not from the menu.

The single mistake to avoid here is doing this after a set has inbound links from elsewhere on the web. The redirects work, but every one of them is a hop you are paying for forever. Get folder names right before launch; that is the whole reason this page exists.

ESC