Skip to content

Navigation

Tabs, groups, and the two page shapes you author by hand.

navigation is the one required field that describes your site’s shape. It is a list of tabs; each tab holds groups; each group holds pages.

json
{
"navigation": {
"tabs": [
{
"label": "Docs",
"groups": [
{ "label": "Get started", "pages": ["index", "quickstart"] },
{ "label": "Authoring", "pages": ["authoring/pages", "authoring/navigation"] }
]
}
]
}
}

Every tab needs at least one group, and every group at least one page. An empty tab is a schema error, not an empty section — so a tab comes into existence at the moment it has real content to hold, not before.

A bare string is a page id — and a page id is not quite the same thing as a file path. It is the identifier Reed derives from the file, and four rules shape it:

  1. Every path segment is slugified independently

    Lowercased, punctuation stripped. Guide/Intro.mdx has the page id guide/intro, not Guide/Intro.

  2. A nested index collapses to its directory

    derived/index.mdx has the page id derived. Write "derived", never "derived/index". A root-level index.mdx is the site root and is written "index".

  3. A frontmatter slug wins outright

    A page whose frontmatter sets slug: custom-route has the page id custom-route, whatever its path.

  4. Some files are not pages at all

    A file whose name begins with _, or one inside a dot-prefixed directory, is not loaded and therefore has no page id.

The written form is still constrained to ^[A-Za-z0-9_-]+(?:/[A-Za-z0-9_-]+)*$ — which permits uppercase, so "Guide/Intro" is a legal string that names no page.

reed check reports a page id that resolves to nothing, naming both what you wrote and the id the file actually has. It is an error, so a navigation entry can never point at nothing.

A group entry can itself be a group object, nesting up to 5 levels:

json
{
"label": "Reference",
"pages": [
{ "label": "CLI", "pages": ["reference/cli/init", "reference/cli/build"] }
]
}

An entry of the shape { "label": "...", "api": true, "spec": "ingestion" } generates a complete set of endpoint pages from one OpenAPI spec. No MDX file exists for these — Reed synthesizes them.

json
{
"label": "Ingestion API",
"groups": [
{
"label": "Ingestion API",
"pages": [{ "label": "Ingestion API", "api": true, "spec": "ingestion" }]
}
]
}

spec names a key declared under api.specs; you may omit it only when api.specs has exactly one entry.

Two structural rules come with it: an API entry must be the only page in its group, and that group the only group in its tab. Reed requires a dedicated tab per spec — generated endpoint pages cannot be mixed alongside hand-written pages in one group.