Navigation: tabs, versions, languages, products, dropdowns
For anything past a single sidebar, navigation can be an object choosing exactly one root pattern instead of a flat array — tabs, versions, languages, dropdowns, or products.
This mirrors Mintlify’s own writedocs.json: “choose one primary organizational pattern at the root level.”
All five container kinds are structurally interchangeable — each owns exactly one of pages, tabs, versions, languages, dropdowns, products, or a bare href, as its content. That symmetry is what lets any of them nest inside any other, to any depth: a tab can contain versions, a version can contain languages, a product can contain versions that contain tabs, and so on — always bottoming out at a pages array (or openapi, see API Reference (OpenAPI)).
tabs
Renders a horizontal navbar with one pill per tab. When there are more tabs than fit, the row slides sideways instead of wrapping: an arrow shows on each side that has more tabs, and the active tab always starts in view. On a touch screen, swipe the row sideways.
{
"navigation": {
"tabs": [
{ "tab": "Guides", "pages": ["index", "getting-started"] },
{ "tab": "API Reference", "pages": ["api/overview"] },
{ "tab": "Blog", "href": "https://blog.example.com" }
]
}
}A tab entry can also be a bare { tab, href } link instead of owning pages — like “Blog” above: it renders as a plain pill that opens href directly (external links open in a new tab), with no pages/sidebar of its own. href works this same way on any of the five container kinds below (versions, languages, dropdowns, products) — one bare-link escape hatch, not something special to tabs.
versions
Renders a version-switcher dropdown.
{
"navigation": {
"versions": [
{ "version": "v2", "label": "v2 (latest)", "tag": "Latest", "default": true, "pages": ["v2/index"] },
{ "version": "v1", "pages": ["v1/index"] }
]
}
}tag adds a badge next to the version name (e.g. “Latest”, “Deprecated”). default picks which version “first page” links resolve to when nothing else determines it. When switching versions, Writedocs tries to keep you on the same page across versions (by position, not by matching file paths — versions commonly live under unrelated folder names) rather than always jumping to the target version’s first page.
languages
Renders a language-switcher dropdown. label controls the display name (defaults to the raw code, like en, if omitted).
{
"navigation": {
"languages": [
{ "language": "en", "label": "English", "pages": ["index"] },
{ "language": "pt-br", "label": "Português", "pages": ["index"] }
]
}
}Every page under a language is in that language, so a language can’t contain another languages list - writedocs validate and the build reject it. Put the languages at one level, with everything else (tabs, versions, …) inside each of them.
products
Renders a product-switcher dropdown — for docs covering several distinct offerings.
{
"navigation": {
"products": [
{ "product": "Core Platform", "pages": ["core/index"] },
{ "product": "Mobile SDK", "pages": ["mobile/index"] },
{ "product": "Status Page", "href": "https://status.example.com" }
]
}
}At the root, the switcher sits in the top bar. Inside a tab or a dropdown, it sits at the top of the sidebar instead, showing each product’s icon and description:
{
"navigation": {
"tabs": [
{ "tab": "Guides", "pages": ["guides/intro"] },
{
"tab": "Platform",
"products": [
{ "product": "Payments", "icon": "credit-card", "description": "Accept cards and wallets", "pages": ["platform/payments/overview"] },
{ "product": "Billing", "icon": "receipt", "description": "Subscriptions and invoices", "pages": ["platform/billing/overview"] }
]
}
]
}
}A page with no sidebar (mode: custom or blank) shows that switcher in the top bar.
dropdowns (as root)
Each entry is its own independent, always-visible navbar dropdown trigger — rather than one trigger listing several items.
{
"navigation": {
"dropdowns": [
{ "dropdown": "Docs", "pages": ["index"] },
{ "dropdown": "API", "pages": ["api/index"] }
]
}
}A tab (or dropdown) that owns dropdowns instead of pages
Any container can own dropdowns instead of pages as its content — it renders as a dropdown-trigger button showing the current selection, instead of a plain pill link:
{
"navigation": {
"tabs": [
{
"tab": "API Reference",
"dropdowns": [
{ "dropdown": "REST API", "pages": ["api/rest/index"] },
{ "dropdown": "GraphQL", "pages": ["api/graphql/index"] }
]
}
]
}
}Nesting containers
Any of the five kinds can contain any other, to any depth - except a language inside another language (see languages), and a third level of tabs (see below). For example, a tab containing versions, where each version has its own tabs bar:
{
"navigation": {
"tabs": [
{
"tab": "API Reference",
"versions": [
{
"version": "v2",
"tabs": [
{ "tab": "Guides", "pages": ["api/v2/guides"] },
{ "tab": "Reference", "pages": ["api/v2/reference"] }
]
}
]
}
]
}
}Tabs inside tabs, like these, get a second row under the first: the outer tabs on top, and the tabs of the active one underneath. Two levels of tabs is the limit, counting through anything in between - writedocs validate and the build reject a third. Use a dropdown or groups for that level instead.
Every branch of the tree is free to be as deep or shallow as that section needs — a product with versions and tabs can sit alongside a sibling product that skips straight to pages.