WritedocsWritedocs

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:

FieldWhat the item holds
pagesA sidebar: pages, groups and links, as in Navigation basics.
tabs, versions, languages, products, dropdownsAnother level of navigation - see Nesting.
hrefNothing: 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.

tab string required

The tab’s name.

icon string

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.

version string required

The version’s name, like v2.

label string

Shown in the switcher in place of version, like v2 (latest).

tag string

A small label next to the name, like Latest or Deprecated.

default boolean

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.

language string required

The language’s code, like en or pt-br. The pages under it are marked as that language, for browsers and screen readers.

label string

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.

product string required

The product’s name.

icon string

Shown before the name. Any icon.

description string

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 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 without a sidebar (mode: frame, custom or blank) 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.

A switcher next to the site name, like versions - for sections that aren’t versions, languages or products.

dropdown string required

The dropdown item’s name.

icon string

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"] }]
      }
    ]
  }
}
page string

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 pages shows 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.

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 no index.mdx).
  • A folder address leads to it when it’s the first page under that folder - /payments/ opens Online payments.
  • llms.txt lists 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.