The content model
What lives in a content root, how routes come out of it, and why it is shaped this way.
A content root is a folder. It can be called anything — docs,
my-product, the-book — and it describes itself completely, so pointing a
build at it is all the build needs. That portability is the point: the folder
can live in another repository, or be cloned on demand, and nothing about the
site is left behind.
the-book/
├── site.yaml # identity, brand, theme, footer (optional)
├── menu.yaml # the top bar (optional)
├── pages.yaml # pages composed from named sections (optional)
├── sections/ # one file per section
│ ├── hero.yaml
│ └── why-us.mdx
├── assets/
├── 01-first-chapter/
│ └── _sidebar.yaml # makes this folder a documentation tree
└── 02-second-chapter/
└── _sidebar.yaml
The two kinds of page
A page is everything below the menubar. There are two kinds, and a menu item can point at either without caring which it got.
Declared in pages.yaml as an ordered list of section names. Renders
top to bottom, no sidebar.
- name: pricing
title: Pricing — Acme
description: Simple per-seat pricing.
sections:
- pricing/hero
- pricing/plansAny folder holding a _sidebar.yaml. Renders as a documentation tree with
left navigation, breadcrumbs, on-this-page and prev/next.
title: Handbook
order: 1
nav:
- introduction
- label: Policies
items: [leave, expenses]Routes
| Source | Route |
|---|---|
| The home destination | / |
pages.yaml → - name: pricing | /pricing |
pages.yaml → - name: solutions/startups | /solutions/startups |
01-guide/index.mdx | /guide |
01-guide/config/database.mdx | /guide/config/database |
assets/logo.svg | served under the site's asset route |
Numeric prefixes are stripped
A folder named 01-getting-started serves /getting-started and is labelled
"Getting started". The prefix is an ordering instruction, not part of the
site's identity, so it never reaches a URL.
Recognised forms are 01-, 1., 02_ and 10 - . A folder genuinely named
2024 keeps its digits — the prefix must be followed by a separator.
Camel case is split too, so 1.MyFirstChapter gives /my-first-chapter, and
acronyms survive: APIReference becomes /api-reference, not
/a-p-i-reference.
What "/" shows
In order:
home:insite.yaml, if set- a page that declares
route: / - the first menu item that resolves to a page
- the first page, or failing that the first guide folder
A sections page picked this way simply becomes /. A guide folder cannot
move — its pages hang below its route — so / redirects to it instead. Either
way the content exists at exactly one URL.
Ordering
When something is listed explicitly in menu.yaml or pages.yaml, that order
wins. Otherwise, for guide folders:
| # | Rule |
|---|---|
| 1 | Listed in menu.yaml or pages.yaml |
| 2 | order: inside the folder's own _sidebar.yaml |
| 3 | A numeric folder prefix |
| 4 | Alphabetical |
Where config lives
site.yaml sits inside the content root, because it is part of what the
site is. Two things do not: where the folder is, and where the app is mounted.
Those belong to the wrapper.
const zellij = createZellijPages({
contentDir: '../../docs/external',
basePath: '/products/acme/docs',
});