Tabs, versions, languages and products
When one sidebar isn’t enough, navigation is an object with one of five lists - tabs, versions, languages, products or dropdowns. Each item in the list has a sidebar of its own:
{
"navigation": {
"tabs": [
{ "tab": "Guides", "icon": "book-open", "pages": ["index", "docs/quickstart"] },
{ "tab": "API reference", "icon": "code", "pages": ["api/overview"] },
{ "tab": "Blog", "href": "https://blog.example.com" }
]
}
}Each item has one of these, for what’s inside it:
| Field | What the item holds |
|---|---|
pages | A sidebar: pages, groups and links, as in Navigation basics. |
tabs, versions, languages, products, dropdowns | Another level of navigation - see Nesting. |
href | Nothing: the item is a link. A link to another site opens in a new tab. |
An item with two of them, like both pages and versions, is an error in writedocs validate.
Every item also accepts page - a page of its own, see An item’s own page - and hidden and searchPublic - see Hidden sections.
Tabs
A row of tabs under the topbar - one per item.
The tab’s name.
Shown before the name. Any icon.
When there are more tabs than fit the row, it scrolls sideways instead of wrapping: an arrow shows on each side that has more tabs, and the current tab is always in view. On a touch screen, swipe the row.
A tab with dropdowns instead of pages opens a menu of them:
{
"navigation": {
"tabs": [
{ "tab": "Guides", "pages": ["index"] },
{
"tab": "SDKs",
"icon": "package",
"dropdowns": [
{ "dropdown": "JavaScript", "pages": ["sdks/js/install", "sdks/js/usage"] },
{ "dropdown": "Python", "pages": ["sdks/python/install", "sdks/python/usage"] }
]
}
]
}
}A second level in the menu
A dropdown in a tab’s menu can hold dropdowns of its own. They open in a flyout beside its entry, with the entry’s name as a heading:
{
"navigation": {
"tabs": [
{ "tab": "Home", "pages": ["index"] },
{
"tab": "Developers",
"dropdowns": [
{
"dropdown": "Resources",
"icon": "book",
"page": "dev/resources",
"dropdowns": [
{ "dropdown": "Webhooks", "icon": "webhook", "pages": ["dev/webhooks/intro", "dev/webhooks/events"] },
{ "dropdown": "Testing", "icon": "flask-conical", "pages": ["dev/testing/intro"] },
{ "dropdown": "Changelog", "icon": "history", "href": "https://example.com/changelog" }
]
},
{
"dropdown": "Platforms",
"icon": "layers",
"dropdowns": [
{ "dropdown": "Marketplaces", "pages": ["platforms/marketplaces/intro"] },
{ "dropdown": "White label", "pages": ["platforms/white-label/intro"] }
]
},
{ "dropdown": "Authentication", "icon": "key-round", "pages": ["dev/auth"] }
]
}
]
}
}- Pointing at an entry - or tapping its arrow - opens its flyout.
- Clicking an entry opens its own page when it has one - like Resources above; its heading in the flyout links there too. An entry without a page - like Platforms - opens its flyout instead.
- Choosing a dropdown in the flyout opens its first page, with its sidebar. The tab, the entry and the dropdown are all highlighted.
- Keyboard: Down and Up move between the menu’s entries, Right opens an entry’s flyout, and Left or Escape go back to the entry.
- Near the right end of the tab row, the flyout opens to the left of the menu, so it stays in view.
- On a phone, the menu that opens from the topbar lists each level - the tab, the entry, the dropdown.
The menu goes two levels deep at most: a dropdown inside a flyout’s dropdown is an error in writedocs validate. Use products or groups for the next level. The flyout is only in a tab’s menu - dropdowns inside dropdowns anywhere else are switchers next to the site name.
Versions
A version switcher next to the site name.
The version’s name, like v2.
Shown in the switcher in place of version, like v2 (latest).
A small label next to the name, like Latest or Deprecated.
The version the site opens on. Without it, the first version is.
{
"navigation": {
"versions": [
{ "version": "v2", "tag": "Latest", "default": true, "pages": ["v2/index", "v2/quickstart"] },
{ "version": "v1", "tag": "Deprecated", "pages": ["v1/index", "v1/quickstart"] }
]
}
}Switching versions keeps the reader on the same page in the other version - the page in the same place in its navigation - rather than sending them to its first page. Versions often live in different folders, so this goes by position, not by file name.
Languages
A language switcher next to the site name.
The language’s code, like en or pt-br. The pages under it are marked as that language, for browsers and screen readers.
Shown in the switcher, like English or Português. Without it, the code is.
{
"navigation": {
"languages": [
{ "language": "en", "label": "English", "pages": ["en/index", "en/quickstart"] },
{ "language": "pt-br", "label": "Português", "pages": ["pt-br/index", "pt-br/quickstart"] }
]
}
}Each language has its own pages, usually in a folder per language. Like versions, switching languages keeps the reader on the same page, by its place in the navigation. Every page under a language is in that language, so a language can’t contain another languages list - writedocs validate rejects it. Put languages at one level, with tabs, versions and the rest inside each language.
Products
A switcher for docs that cover several products.
The product’s name.
Shown before the name. Any icon.
A short line under the name, in the switcher at the top of the sidebar.
{
"navigation": {
"products": [
{ "product": "Payments", "icon": "credit-card", "pages": ["payments/index"] },
{ "product": "Billing", "icon": "receipt", "pages": ["billing/index"] },
{ "product": "Status page", "href": "https://status.example.com" }
]
}
}Where the switcher shows depends on where the products are:
-
At the top level, it’s next to the site name.
-
Inside a tab or a dropdown, it’s at the top of the sidebar, with each product’s
iconanddescription:{ "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 without a sidebar (
mode: frame,customorblank) doesn’t show it - a hand-built page, like a product overview, links to the products itself. On a phone, the menu still lists them.
Dropdowns
A switcher next to the site name, like versions - for sections that aren’t versions, languages or products.
The dropdown item’s name.
Shown before the name. Any icon.
{
"navigation": {
"dropdowns": [
{ "dropdown": "Docs", "icon": "book-open", "pages": ["index", "docs/quickstart"] },
{ "dropdown": "API", "icon": "code", "pages": ["api/index"] }
]
}
}Inside a tab, dropdowns are that tab’s menu instead - see Tabs.
Nesting
Any of the five can go inside any other, as deep as your docs need. A tab with versions, each version with its own tabs:
{
"navigation": {
"tabs": [
{ "tab": "Guides", "pages": ["index"] },
{
"tab": "API reference",
"versions": [
{
"version": "v2",
"tabs": [
{ "tab": "REST", "pages": ["api/v2/rest/overview"] },
{ "tab": "Webhooks", "pages": ["api/v2/webhooks/overview"] }
]
},
{ "version": "v1", "pages": ["api/v1/overview"] }
]
}
]
}
}Each branch goes as deep as it needs: a product with versions and tabs can sit next to a product that has just pages, like v1 above.
Two limits:
- Two levels of tabs. Tabs inside tabs are a second row, under the first: the outer tabs on top, the tabs of the current one below - counting through anything in between, like the versions above. A third level is an error in
writedocs validate; use dropdowns or groups for it. - One level of languages. See Languages.
On a phone, the tabs and switchers move into the menu that opens from the topbar, with the sidebar.
An item’s own page
Any tab, version, language, product or dropdown can have a page of its own - an overview of what’s in it. Choosing the item opens that page, instead of the item’s first page.
{
"navigation": {
"tabs": [
{ "tab": "Home", "pages": ["index"] },
{
"tab": "Payments",
"dropdowns": [
{
"dropdown": "Online payments",
"icon": "globe",
"page": "payments/online",
"products": [
{ "product": "Checkout", "pages": ["payments/checkout/intro", "payments/checkout/setup"] },
{ "product": "Payments API", "pages": ["payments/api/intro"] }
]
},
{
"dropdown": "In person",
"icon": "store",
"page": "payments/in-person/intro",
"pages": ["payments/in-person/intro", "payments/in-person/terminals"]
}
]
},
{
"tab": "Guides",
"page": "guides/overview",
"pages": [{ "group": "Guides", "pages": ["guides/start", "guides/advanced"] }]
}
]
}
}A page, written like the ones in pages: "payments/online" is payments/online.mdx. Not on an item with href - that’s a link, and writedocs validate rejects it. A page that doesn’t exist is an error in writedocs validate.
Where the page opens depends on whether a sidebar lists it:
-
A sidebar lists it - like In person above, whose page is the first in its own sidebar: it opens there, highlighted, like any page.
-
No sidebar lists it - like Online payments and Guides: it opens inside the item. The item is the current one on its level, and below it:
- An item with
pagesshows its sidebar, with nothing highlighted. The page’s Next link is the sidebar’s first page. - An item holding products shows the product switcher at the top of the sidebar with the item’s name, and no product chosen. Choosing one opens that product’s first page.
- An item holding tabs shows them in a second row with none chosen; versions, languages and dropdowns show their switcher with the item’s name. With nothing for a sidebar, the page has none, like
mode: frame.
On a phone, the menu lists the item’s options right under it.
Switching the version or language on such a page goes to the same item in the other one: its own page, or its first page when it has none. When the other version or language isn’t organized the same way, it goes to its first page.
- An item with
The page also counts where the navigation names it - before the item’s own pages:
- When the first item has a page, the site’s address
/opens it (if the site has noindex.mdx). - A folder address leads to it when it’s the first page under that folder -
/payments/opens Online payments. llms.txtlists it first under the item’s heading.
Where a reader lands
The site’s address, /, shows the home page (index.mdx). Without one, it sends the reader to the first page of the first item - or of the default version.
Clicking a tab, version, language, product or dropdown opens its own page, when it has one, or its first page. Opening the address of a folder with no page of its own - /docs/payments/ - sends the reader to the first page under it. See Redirects.