Skip to main content

Content

Multi-product docs

Host docs for multiple products on one site — Cloud / Enterprise / Self-hosted, each with its own nav and content.

Teams with multiple products often face a choice: run N separate docs sites (one subdomain per product, painful to maintain, no shared chrome), or cram everything into a single navigation tree (confusing for readers who care about only one product).

NookDocs gives you a third option: one site, N product switchers. A dropdown next to the logo lets readers pick their product; the sidebar, URL prefix, and (future) chrome overrides update accordingly.

Declare products

In Configurations → Products (or direct nookdocs.config.json):

{
  "products": [
    { "product": "Cloud", "default": true, "description": "Managed hosting" },
    { "product": "Enterprise", "description": "Self-hosted", "tag": "Beta", "status": "beta" }
  ]
}
productstringrequired

Product name. Used as both the switcher label and the default URL slug.

descriptionstring

One-line description shown below the product name in the switcher dropdown. Helps readers pick the right one.

defaultboolean

The product loaded when a reader lands on / without a product prefix. At most one default.

slugstring

URL prefix override. Defaults to the slugified product name (e.g. "Cloud Connect" → cloud-connect).

Repo layout

your-repo/                        # the docs base
├── nookdocs.config.json
├── introduction.mdx              ← Cloud (default), no prefix
├── quickstart.mdx
├── api/
│   └── overview.mdx
└── enterprise/                   ← Enterprise, URL prefix /enterprise
    ├── introduction.mdx
    ├── installation.mdx
    └── admin/
        └── sso.mdx

Readers land on / → see Cloud docs. They pick "Enterprise" from the switcher → URL becomes /enterprise/ → Enterprise tree loads. Same pattern for every product.

When to use products vs versions vs tabs

Products

Different audiences. Each product has its own concepts, onboarding, and audience. Cloud users and Enterprise users rarely read the same pages.

Versions

Same product, multiple generations. v1 / v2 / v3 readers are mostly the same people who happen to be locked onto different releases.

Tabs

Same product, different tasks. Guides / API Reference / Changelog — everyone reads all tabs, they just organize information.

Combining them:

  • Products × versions: Cloud v1 / v2 / v3 AND Enterprise v1 / v2. Nest versions[] inside each product. Per-product versions ship as part of the Phase 3+ follow-up (see Navigation schema).

  • Products × languages: Cloud in en / tr AND Enterprise in en / tr. Nest languages[] inside each product (planned).

Shared vs product-specific chrome

Elements that look the same across products (logo, brand colors, theme) are defined at the top level of the config and apply everywhere. Product-specific chrome lives on the product entry:

{
  "products": [
    {
      "product": "Cloud",
      "default": true,
      "navbar": {
        "links": [{ "label": "Pricing", "href": "https://example.com/cloud/pricing" }]
      },
      "colors": {
        "primary": "#3b82f6"
      }
    },
    {
      "product": "Enterprise",
      "navbar": {
        "links": [{ "label": "Contact sales", "href": "mailto:sales@example.com" }]
      },
      "colors": {
        "primary": "#8b5cf6"
      }
    }
  ]
}

Per-product navbar / colors / footer / banner are in the schema today but the renderer uses the top-level config for every product. Per-product chrome wiring ships in a follow-up sprint.

Shared chrome: global

When something must appear on every product (status page anchor, global changelog, corporate privacy link), use the top-level global config instead of duplicating it:

{
  "global": {
    "anchors": [
      { "anchor": "Status", "icon": "activity", "href": "https://status.example.com" }
    ],
    "dropdowns": [
      {
        "dropdown": "Changelog",
        "icon": "history",
        "pages": ["docs/changelog/2026-q1"]
      }
    ]
  }
}

global.anchors + global.dropdowns render at the top of every sidebar, regardless of active product. The Configurations → Global nav section lets you edit anchors from the dashboard; dropdowns require direct nookdocs.config.json edit.

Onboarding a reader to the right product

Link pages across products using absolute paths:

For self-hosted docs, see [Enterprise → Installation](/enterprise/installation).

When a reader lands from an ad or blog post, they'll arrive on a specific product's page; the switcher shows them which one. This is usually enough context — but for marketing landing pages you may want to add a Banner that surfaces the product switcher:

<Banner variant="info">
  Reading Cloud docs. Looking for self-hosted? [Switch to Enterprise](/enterprise/).
</Banner>

Migration: single-product → multi-product

Wrap existing content into a default product

Your existing docs/ stays where it is. Add a default product entry: { "product": "Your Product", "default": true }. Nothing changes visually because the default product has no URL prefix.

Add the second product's folder

docs/enterprise/ (or whatever slug you pick). Drop at least an introduction.mdx so the switcher has somewhere to route.

Add the second product entry

{ "product": "Enterprise", "description": "Self-hosted" } in products[]. Save config, wait for sync, the switcher appears in the navbar.

Cross-link where useful

Banners on product-specific pages that point readers to the other product when relevant. "This feature is Cloud-only. For Enterprise, see Self-hosted SSO."

No existing URLs break — the default product's content still serves at /. Non-default product URLs gain the prefix and start resolving only after you add the folder + config entry.

Search and AI Assistant

Search (Postgres FTS) indexes every page regardless of product. A Cloud reader searching "SSO" will see both Cloud SSO and Enterprise SSO results. Per-product search scoping ships when the full routing layer lands.

The AI Assistant has the same caveat — it retrieves from the full docs tree. For clean-separation customers, disable the widget on one product and leave it on the other via ai.assistant.enabled per scope (planned).

Was this page helpful?

Last updated August 7, 2026