URL Paths and Slugs

Every published page has exactly one live URL. Leed also keeps a record of every URL it has ever assigned that page, and serves a permanent redirect from each of them. That is the fact underneath everything on this page: you are free to rename, re-slug and reorganize, because the old addresses keep working.

You never type a URL in Leed. You set the pieces — a page type’s slug, a page’s slug, and for a documentation set, its folder position — and Leed composes the rest.

How a URL is composed

There are two shapes, and only two.

Ordinary page      /{page-type-slug}/{page-slug}/
Documentation page /{page-type-slug}/{folder}/{folder}/{page-slug}/

Every Leed URL composes with a trailing slash. An empty part is skipped rather than producing a doubled slash, and a page whose slug is empty composes to its section root — /docs/ — and publishes as that section’s index file.

The difference is the middle. A documentation page’s folder segments come from its position in its set’s left navigation menu, not from any field on the page. Each folder item’s name is slugified into one segment. Not its tooltip; not its title.

flowchart TD
  UNIQ{"Is that path already taken<br/>by another page?"}
  SUFFIX["Append -2, then -3, then -4 …"]
  COMPOSE["Join the parts with slashes<br/>and add a trailing slash"]
  OUT["/type/folder/slug/"]

  subgraph ORD["An ordinary page"]
    direction TB
    OPT["Its page type's slug"]
    OT["The page's title"] --> OS["slugified"]
  end

  subgraph DOC["A documentation page"]
    direction TB
    DPT["Its page type's slug"]
    DF["Each ancestor folder's name<br/>in the left menu"] --> DFS["slugified, one segment each"]
    DT["The page's title"] --> DS["slugified"]
  end

  OPT --> UNIQ
  OS --> UNIQ
  DPT --> UNIQ
  DFS --> UNIQ
  DS --> UNIQ

  UNIQ -->|yes| SUFFIX
  SUFFIX --> UNIQ
  UNIQ -->|no| COMPOSE
  COMPOSE --> OUT

The uniqueness check is a real loop: Leed keeps appending an incrementing suffix until the composed path is free across your whole workspace.

A page’s stored path value holds only the folder portion — never the page-type slug, never its own slug. For every non-documentation page type it is empty, because those sections are flat by design.

Where each slug comes from

SegmentSourceSet whereEditable after publish?
Page-type slugThe page type’s own slug fieldSettings → Page TypesNo, once the section has published pages — the field goes read-only
Documentation folder segmentsThe name of each folder the page sits inside, in the set’s left menu, slugifiedThe Documentation section of the Design workspaceYes — by moving the page or renaming the folder, which moves every page beneath it
Page slugThe page’s own slug, defaulted from its titleThe editor’s Page settings tab, under Page DetailsYes, freely
Trailing slashAlways added——

How a slug is derived

At creation, a page’s slug is always the slugified title. Nothing else.

Slugification is not a straight lowercase-and-hyphenate. It splits runs of capitals, which produces results you would not predict from the title:

"Getting Started"             →  getting-started
"Pricing & Plans"             →  pricing-and-plans
"What is Leed?"               →  what-is-leed
"Importing an OpenAPI Spec"   →  importing-an-open-api-spec

That last one is the one to remember. Check the slug after creating any page whose title contains an acronym, an ampersand or punctuation, and fix it before anything links to it — a link written against the wrong slug is an inbound URL you then have to keep redirecting.

If the composed path is already taken, Leed appends -2, then -3, and so on until it is free. This applies at creation and again every time you edit the slug, and it is silent — type a slug another page already owns and you get the suffixed version back with no message. Read the field after you type in it.

You cannot choose a page’s id either

A page’s id is minted by the server when the page is created and returned to you. It cannot be supplied — the create schema explicitly excludes it — and it never changes afterwards.

That id is what internal links and menu entries actually reference. A link written as pageid: plus an id resolves to whatever URL that page has at build time, which is why renaming a page never breaks a link inside Leed.

The practical consequence is for anyone importing content in bulk: the work is necessarily two passes. Create every page stub first, collect the ids that come back, and only then author bodies whose internal links point at them. A link written before its target exists cannot resolve, and at build time it renders as plain unlinked text rather than as an error.

The three kinds of path record

TypeWhat it meansWhen it is createdWhat a visitor gets
CurrentThe page’s live URLWhen the page is first published, and at every later publicationThe page
NextA reserved URL waiting to go liveThe moment you change the slug of a published page, so nothing else can claim it before you publishNothing — it is a reservation, not a route
AliasA URL the page used to have, or one you added by handWhen a slug change is published, or when you add a redirectA permanent (301) redirect to the current URL

Changing a slug safely

Renaming a published page is a four-step sequence, and only the last step is visible to the outside world.

  1. Edit the slug in the editor’s Page settings tab, under Page Details.
  2. The new URL is reserved immediately as a next record, so no other page can take it in the meantime.
  3. Nothing changes on your live site. The page keeps serving at its current URL until you publish it.
  4. Publishing promotes the reservation. The next record becomes current, and the URL it replaced becomes an alias that redirects permanently.

Change your mind before publishing and set the slug back to what it was, and the reservation is released — Leed notices the recomputed path matches the live one and deletes the next record.

The editor's Page settings tab with the Page Details section open and the Slug field in focus

There is no composed-URL preview beside the slug field. To see the full address a page will publish at, use Settings → URL paths & redirects, described at the end of this page.

When a slug change is refused

Only one thing hard-refuses a slug edit: a path that would sit exactly on another page type’s root. Try it and you get a 409 reading “Cannot place a page at ‘/docs/’ — that is the root path of the ‘Documentation’ page type”, naming the page type it collides with.

That rule is deliberately narrow. Overlap and nesting are fine. A page of your docs type parked at /docs/api/1.2.0/notes/ is allowed even when an API set roots at /docs/api/1.2.0/ — only landing exactly on that root is refused, because such a page would shadow the whole set’s landing address. A page at its own type’s root is also fine; that is how a section’s index page works. And a page type whose slug is empty — one that roots at the site root, like your homepage’s type — is exempt on both sides.

Everything else that looks like a collision is not refused, it is renumbered. Typing a slug another page already owns gives you the -2 form. That silence is worth naming, because it is the difference between the two ways a URL can change:

  • Editing a slug de-duplicates silently.
  • Moving a documentation page by dragging it in the menu refuses outright, with 409 "Cannot move page: the path '…' is already in use by another page". A folder move can relocate dozens of pages at once, so quietly renumbering some of them would be worse than stopping.

Moving a documentation page

State the rule before the mechanics, because this is the one most often broken by anyone creating pages programmatically:

A documentation page’s URL is its folder location in the left navigation menu, and the left menu is the only thing that sets it.

Each folder item’s name becomes one slugified segment. A path supplied when you create such a page is ignored by design, and the update endpoint does not accept a path at all — the code comment says why, and it is worth repeating: a path written straight onto the page record would not stage a matching URL row, so the CMS and the published site would immediately disagree about where the page lives.

So you relocate a documentation page by moving its menu item and saving the menu. Never by editing a field. Never by moving the file in your repository.

Getting that wrong is not a cosmetic problem. A page record whose stored path disagrees with its menu position breaks the paths table, the redirects generated from it, the sitemap, analytics continuity for the page, its breadcrumbs and its previous/next links — and can trip the database’s own uniqueness constraint on paths. Folders Set Your URLs covers the move itself, including what is staged versus committed when a folder rename relocates a whole subtree.

Where a page’s file lands in your repository

Every published page is a markdown file in your site repository, and its location mirrors its URL:

src/{page-type-slug}/{folder}/{folder}/{page-slug}.md

A page with an empty slug becomes index.md in its folder. Sidecar files — an API page’s OpenAPI document, for instance — sit beside the markdown with the same name and a different extension, so a move renames both together.

This is the same rule as the URL, stated from the repository side, and it is enforced rather than merely conventional: the page-type slug plus the folder path plus the page slug must equal the path derived from the file’s own location. The stored folder path is constrained to lowercase segments joined by single slashes, with no leading or trailing slash, and the import tooling throws outright when a documentation file does not sit under its page type’s slug root.

Useful to know if you read your repository. Mandatory if you write into it — see Your Site Repository.

Seeing every URL you own

Settings → URL paths & redirects, in the Content model group, is a read-only inventory of what Leed has composed for you. It has two sections, each with a count:

  • Redirects — every alias row: the old URLs, still serving 301s.
  • Permalinks — every live URL, sorted alphabetically, so nested documentation paths group under their sections.

There is nothing to edit here. Slugs are edited on the page, and redirects are added in the page’s own Redirects section — both in the editor’s Page settings tab.

Settings, URL paths and redirects, showing the Redirects section with its count above the Permalinks section

A reserved URL becomes the live one at the page’s next publication — see Publishing and Scheduling a Page. What happens to the URL it replaced, and how to add a redirect deliberately for a migration or a memorable short path, is Aliases and Redirects.

ESC