Skip to content

Navigation

The sidebar is built from the content tree. One optional meta.json per directory changes the order and the labelling; every directory without one still gets a sidebar.

Why a file, and not a filename

A filename cannot express a separator, an external link or a directory title. Numeric prefixes — 01-installation.md — carry exactly one piece of information, and a docs sidebar needs four. They also put the sort weight in the URL, so reordering the tree changes every link into it.

meta.json is validated strictly: an unrecognised key fails the build rather than being ignored. The keys people reach for are page, order and items, none of which exist, and each of them would otherwise fail as a sidebar that quietly drops half the file.

Every entry kind

content/api/meta.json
{
  "title": "API Reference",
  "pages": [
    "index",
    "authentication",
    "---Advanced---",
    "...webhooks",
    "...",
    { "title": "Status page", "href": "https://status.example.com" }
  ]
}
EntryMeaning
"authentication"A file or subdirectory in this directory, in this position
"---Advanced---"A non-interactive separator with the enclosed label
"..."Everything not named explicitly. At most one per file
"...webhooks"Expand the webhooks subdirectory inline, with no group wrapper
{ "title", "href" }An arbitrary link. external is inferred from the href

A name is the filename without its extension, or the directory name — "authentication" addresses authentication.md, "...webhooks" addresses webhooks/. Anything unnamed is dropped unless a "..." says where to put it.

"...webhooks" discards the group wrapper, not the directory's own page: the link to webhooks/index.md is spliced in ahead of its children, because the group heading was the only thing carrying that href and the route exists either way.

With no pages list

Omit pages and the directory sorts by frontmatter order ascending, then by title. Entries without an order sort after every entry that has one, and the title comparison is pinned to the en locale so sidebar order cannot depend on the build machine. That is exactly what a lone "..." does.

label wins over title for the sidebar entry when a page sets one — sidebars are narrow. Both are frontmatter fields; see Writing content.

Group headings

A group heading takes its meta.json title, else its index.md label, else its index.md title, else the directory name humanised — getting-started becomes Getting Started.

This site's own sidebar

Look left. The sidebar you are reading is the example. Its root meta.json names the five top-level entries in reading order, then a ---Project--- separator, then three external links:

content/meta.json
{
  "pages": [
    "index",
    "getting-started",
    "guides",
    "reference",
    "internals",
    "---Project---",
    { "title": "GitHub", "href": "https://github.com/wavedotso/wave-docs" },
    { "title": "npm", "href": "https://www.npmjs.com/package/@waveso/docs" },
    {
      "title": "Changelog",
      "href": "https://github.com/wavedotso/wave-docs/blob/main/CHANGELOG.md"
    }
  ]
}

Each of the three sections has a meta.json of its own, and each sets a title: Getting started, Guides, Reference. Without it the headings would read Getting Started and the sentence case would be wrong in one place out of three — the humanised name capitalises every word.

When it fails

Naming an entry that resolves to nothing fails the build. The message carries the path to the meta.json, the offending entry and the names that are actually there:

@waveso/docs: /app/content/reference/meta.json lists "confguration", which does not exist. Available entries: components, configuration, entry-points, errors, stability.

Two other entries are build errors for the same reason — they look deliberate and are always typos. Naming the same child twice renders the page twice, both copies highlighted as the current page. A second "..." has no honest reading, because a directory has a single set of unnamed pages. Both throw invalid-meta; see Errors.

Three things are deliberately not errors. A draft named in pages resolves and is not emitted, so the build does not break and mend as includeDrafts is flipped. A group that turned out to be empty is dropped. A separator left standing over nothing — the group after it emptied, or another separator follows — is dropped with it, which cannot be left to the author to notice: previewing with drafts on is exactly the mode where the group is not empty.

Nav nodes are ordinary data. DocNavNode is the union of page, group, separator and link, and a shell that wants to render them itself takes them as props — see Layout.