Navigation
How the sidebar is built from folders, meta.json, and frontmatter.
The sidebar is your folder structure. There is no separate navigation file to keep in sync, and no route list to register.
resources/docs/
├── index.md → /docs
├── why.md → /docs/why
└── billing/
├── meta.json
├── index.md → /docs/billing
└── invoices.md → /docs/billing/invoices
A folder becomes a collapsible group. index.md inside it becomes that group's own page.
A page's sidebar label is its title, falling back to the first heading, then to the
file name.
Ordering
Without any configuration, siblings are sorted by frontmatter order first, then
alphabetically by file name:
---
title: Invoices
order: 1
---
order is a blunt tool once a folder has more than a handful of pages. For anything
larger, list the pages explicitly.
meta.json
Drop a meta.json in a folder to control it:
{
"title": "Billing",
"defaultOpen": true,
"pages": ["index", "invoices", "refunds"]
}
| Key | Purpose |
|---|---|
title |
Group label in the sidebar. Defaults to the folder name, title-cased. |
defaultOpen |
Open the group on first load. A group containing the current page always opens. |
pages |
Explicit order. Entries are file names without .md, relative to this folder. |
access |
Gate the whole folder. See Gating. |
Names in pages are relative to the folder the file sits in, so "invoices", not
"billing/invoices".
pages orders the sidebar; it does not filter it. Anything you leave out is still shown,
appended after the listed pages in alphabetical order. To keep a page out of the sidebar
entirely, gate it with access.
Keep the rest
"..." stands for every page not named explicitly, and controls where those pages
land. Without it they go to the end, so use it to put them somewhere else:
{
"pages": ["index", "...", "changelog"]
}
Here index is first, changelog is last, and everything else falls in between
alphabetically.
Section headings
An entry wrapped in triple dashes becomes a non-clickable label, for breaking a long list into sections:
{
"pages": [
"index",
"---Billing---",
"invoices",
"refunds",
"---Reference---",
"webhooks"
]
}
Consecutive and trailing separators are trimmed automatically, so a section whose pages are all hidden by gating does not leave a heading behind.
External and custom entries
An object adds a link that is not a file in this folder:
{
"pages": [
"index",
{ "title": "Changelog", "slug": "changelog" },
{ "title": "API reference", "href": "https://api.example.com" }
]
}
slug builds a docs URL; href is used as written.
_meta.md
_meta.md is the same idea in frontmatter, for when you only need access or a title
and would rather not add JSON:
---
title: Billing
access: auth
---
_meta.md is never rendered as a page. When both files are present, _meta.md wins for
access.
Gating and the sidebar
Pages a reader cannot see are removed from the sidebar, not greyed out, and a group with nothing visible left in it disappears entirely. See Gating.
Two files, one URL
billing.md and billing/index.md both resolve to /docs/billing, as do two pages
sharing a frontmatter slug. vellum:build refuses to build and names both files
rather than letting one silently win.