Skip to main content

Navigation models

Navigation is the only part of your information architecture the reader can see. A sidebar is a claim about how the product is organised, and if that claim does not match how people think about the product, no amount of search tuning rescues it. Pick the model deliberately, write it down as an explicit configuration, and change it on purpose.

Which navigation model should we use?

Group by what the reader knows when they arrive. If they arrive knowing which product they use, group by product. If they arrive knowing what they are trying to achieve, group by task. Almost every real documentation set needs a primary model plus one secondary path, and the mistake is trying to serve both with the same tree.

ModelTop level looks likeWorks whenFails when
By product or componentBilling, Identity, WebhooksUsers adopt one component at a time; teams own componentsUsers do not know which component owns their problem
By task or journeyInstall, Configure, Deploy, MonitorThere is one dominant path through the productSeveral audiences take different paths
By audienceDevelopers, Administrators, AnalystsAudiences share almost no pagesAudiences overlap; pages get duplicated per audience
By lifecycle stageEvaluate, Build, OperateLong adoption cycle with distinct phasesReference content, which belongs to no phase

The reliable combination for software products is product at the top level, content type inside each product, and a task-shaped landing page that cross-cuts. That way the tree is owned (each top-level branch has a team) while the entry points are goal-shaped.

Two constraints worth holding to. Keep the tree three levels deep at most: category, subcategory, page. A fourth level means the second level is wrong. And keep any single list under about a dozen items, because longer lists stop being scanned and start being searched — at which point the sidebar is doing no work.

Explicit sidebars beat autogenerated ones

Autogeneration from the filesystem is convenient during the first week and a liability afterwards. It couples URL structure to sidebar order, makes reordering a rename, and hides accidental additions: a page dropped into a directory appears in the navigation with no review.

Write the sidebar explicitly. It is a file, it is reviewed, and it is the one place where a reader can see the whole intended structure:

// sidebars.js
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
productSidebar: [
'intro',
{
type: 'category',
label: 'Get started',
link: {
type: 'generated-index',
slug: '/get-started',
description: 'Install the CLI, authenticate, and make a first request.',
},
items: ['get-started/install', 'get-started/authenticate'],
},
{
type: 'category',
label: 'Webhooks',
link: {type: 'doc', id: 'webhooks/overview'},
items: [
'webhooks/subscribe',
'webhooks/verify-signatures',
{type: 'autogenerated', dirName: 'webhooks/events'},
],
},
{
type: 'link',
label: 'API reference',
href: '/api/',
},
],
};

export default sidebars;

Three details in that snippet earn their keep. A category can link to a real overview page (type: 'doc') or to a generated index card grid (type: 'generated-index') — an empty category header that does nothing when clicked is a dead end, and readers do click it. autogenerated is still correct for genuinely uniform sets such as one page per event type, where ordering is alphabetical and additions need no editorial thought. And type: 'link' reaches across to a different docs instance or an external system without pretending it is the same tree.

When the tree needs to split

If two documentation sets have different audiences, different versioning cadences or different owners, they should be separate plugin instances rather than separate branches of one sidebar. In Docusaurus 3 that is a second instance of the docs plugin:

plugins: [
[
'@docusaurus/plugin-content-docs',
{
id: 'api',
path: 'api',
routeBasePath: 'api',
sidebarPath: './sidebarsApi.js',
editUrl: 'https://github.com/example/docs/tree/main/',
},
],
],

Separate instances get separate versioning, separate sidebars and separate URL namespaces, which is exactly what you want when the API reference ships on the API's release cycle and the user guide does not. See versioning strategies for how that plays out.

The paths that are not the sidebar

The sidebar carries fewer readers than you think. Most arrive from a search engine, deep in the tree, with no context. Design for that arrival:

  • Breadcrumbs on every page, so the reader can locate themselves in one glance. Docusaurus renders them by default from the sidebar; that is another argument for the sidebar being correct.
  • A next step at the end of every task page. The single most-used navigation control in documentation is the link at the bottom of the page the reader just finished.
  • Sequential pagination that follows a real reading order. Override it with pagination_next and pagination_prev in frontmatter where the sidebar order is not the learning order.
  • Landing pages with intent, not lists. A category index that repeats the sidebar is wasted. Say who the section is for and what the three most common jobs are.

Changing navigation without breaking the web

Navigation changes move URLs, and moved URLs break inbound links, support macros and in-product help. Before you reorganise, read redirect mapping and treat every path change as a redirect obligation.

You can reduce that obligation at design time by decoupling URLs from the tree: give each page a stable slug based on what it is, not where it currently sits, so that moving it between categories does not change its address. That is a naming decision, and it is the subject of naming and controlled vocabulary.