Skip to main content

Content

Versioning

How to organize v1 / v2 / v3 content in your repo, route readers between versions, and retire deprecated APIs.

Docs sites for long-lived products eventually need to version themselves. NookDocs versioning is URL-prefix based — your default version serves at /, other versions at /<slug>/... — and content lives side-by-side in the same repo under folders that match the slugs.

Two questions, one answer

How do readers switch?

A dropdown in the navbar shows every non-hidden version. Click → the URL changes to that version's prefix. Active version is inferred from the URL on every page load.

Where does the content live?

Alongside your existing docs, under folders named after each version's slug. The default version has no folder prefix — it's the root of your docs tree.

Declare versions

Open Configurations → Versions in the dashboard (or edit nookdocs.config.json directly):

{
  "versions": [
    { "version": "v3", "default": true, "tag": "Latest", "status": "stable" },
    { "version": "v2", "status": "stable" },
    { "version": "v1", "tag": "Deprecated", "status": "deprecated" }
  ]
}

Exactly one version may carry default: true. Validation rejects configs with zero or multiple defaults.

Each entry's URL slug is the version label lowercased (override with slug: "..." if you want something custom). The default version's URL has NO prefix — it serves at /. Non-default versions serve at /<slug>/....

Repo layout

This is the layout for the config above:

your-repo/                        # the docs base
├── nookdocs.config.json
├── introduction.mdx              ← v3 (default), no prefix
├── quickstart.mdx
├── guides/
│   └── authentication.mdx
├── v2/                           ← v2, URL prefix /v2
│   ├── introduction.mdx
│   ├── quickstart.mdx
│   └── guides/
│       └── authentication.mdx
└── v1/                           ← v1, URL prefix /v1
    ├── introduction.mdx
    └── guides/
        └── authentication.mdx

URLs resolve like this:

URLReads from
/introduction.mdx (v3)
/guides/authenticationguides/authentication.mdx (v3)
/v2/v2/introduction.mdx
/v2/guides/authenticationv2/guides/authentication.mdx
/v1/v1/introduction.mdx

The sync pipeline treats v2/ as a peer of the base content — same MDX rules, same component allowlist, same frontmatter fields. There's no "v2 mode" to toggle.

Navigation per version

The current version of NookDocs renders the top-level navigation config for every version's sidebar. Per-version navigation trees (so the v2 sidebar can differ from v3) is queued for a follow-up sprint — see the Navigation V2 roadmap.

For now, your navigation is shared across versions. Pages that only exist in one version simply 404 on the others — use hidden: true in frontmatter to drop a page from the sidebar without removing the file.

Writing tips

Add a new version

Snapshot the current docs

Copy the base content into a v2/ folder at the base. The old version's content is frozen; new work continues at the base.

Mark the old version in config

Add { "version": "v2", "status": "stable" } to versions[] — alongside the existing default entry.

Commit + push

The sync pipeline ingests the new folder. Visitors at /v2/... see the snapshot immediately.

Deprecate an old version

Edit the version's entry to add a red tag and a deprecation banner on the version's pages:

{
  "version": "v1",
  "tag": "Deprecated",
  "status": "deprecated"
}

Status deprecated colours the badge red in the navbar dropdown. For a visible warning on every v1 page, add a frontmatter banner to the v1/*.mdx files or use a Banner component at the top of each doc.

Retire a version

Three options depending on how clean you want the break:

  1. Hide from the switcher: set hidden: true on the entry. URLs still resolve, but the dropdown doesn't show v1. Good for "read-only legacy, don't surface it."

  2. Redirect to the latest: add a redirects entry for /v1/*/*. The old URLs bounce to the current version.

  3. Delete: remove the v1/ folder and the versions[] entry. Returns 404 on /v1/... URLs. Add a 404 redirect (see Redirects) if you want a softer landing.

OpenAPI per version

API docs usually track their own version independently. Your api.openapi can point at different specs per version by putting the spec file next to each version's content:

your-repo/                   # the docs base
├── openapi.json             ← v3 spec (default)
├── v2/
│   └── openapi.json         ← v2 spec
└── v1/
    └── openapi.json         ← v1 spec

And reference them in the version-scoped navigation (once per-version nav ships). Until then, use a single top-level api.openapi that points at the latest, and surface legacy specs as direct download links inside the deprecated version's docs.

AI Assistant + versioning

The chat widget's RAG retrieval (via Postgres FTS) indexes every page in the project, including version-prefixed ones. A user asking "how do I authenticate" will get results from whichever version matches best — not necessarily the active one.

Two mitigations:

  1. Pre-pend the active version to queries (planned) — the widget will detect /v2/ in the URL and scope retrieval to docs/v2/*.

  2. Disable the widget on deprecated versions: hidden: true on the version entry + config.ai.assistant.enabled: false if the version is hosted separately.

Was this page helpful?

Last updated August 7, 2026