Adding Versioned Docs to Docusaurus

When an internal API or platform ships breaking changes, engineers consuming an older release need documentation that matches the version they run — not the bleeding edge. Docusaurus has first-class versioning that snapshots your docs at each release so multiple versions are served side by side. This how-to walks through cutting versions, configuring the version dropdown, and keeping CI healthy. It builds on Docusaurus Setup & Customization within the Developer Portal Architecture & Frameworks strategy.

Prerequisites

  • Node.js 20+ and an existing Docusaurus 3.x site (@docusaurus/core 3.5.0+).
  • The classic preset configured with a docs instance and a working sidebars.js.
  • A Git repository with a branching/tagging convention for releases.
  • Current, unversioned content under docs/ that builds cleanly with npm run build.

Versioning only behaves predictably when the source it snapshots is already clean, so the prerequisites are really one idea: a green-building docs/ tree plus a release tagging convention that decides when you cut a snapshot.

Prerequisites for versioning A clean-building docs tree and a release tagging convention are the two prerequisites for cutting versions. docs/ builds clean release tag convention Ready to cut versions deterministic snapshots
A snapshot is only as good as the tree it freezes, so fix build warnings before cutting the first version.
# Requires @docusaurus/core >= 3.5.0
npx docusaurus --version   # 3.5.x

Exact Configuration

The mental model is a snapshot: cutting version 1.0 copies the live docs/ into a frozen versioned_docs/version-1.0/, records the label in versions.json, and leaves docs/ as the unreleased “next”. The dropdown then reads the registry to route readers between them.

How cutting a version snapshots the docs Cutting a version copies docs into a frozen versioned_docs directory, appends to versions.json, and feeds the navbar dropdown. docs/ (next) live editing versioned_docs/ version-1.0 frozen versions.json registry Version dropdown navbar item Reader
Once frozen, a version is immutable; day-to-day edits go to docs/ and become the next snapshot.
  1. Cut your first version snapshot. This copies the current docs/ into versioned_docs/version-1.0/, snapshots the sidebar, and appends the label to versions.json. After this, docs/ becomes the “next”/unreleased version.

    # Requires @docusaurus/core >= 3.5.0
    npm run docusaurus docs:version 1.0
    # Creates: versioned_docs/version-1.0/, versioned_sidebars/, versions.json
    
  2. Configure how versions are labeled and routed in docusaurus.config.js. Setting lastVersion and an explicit versions map controls which version is the default and what each is called in the UI.

    // docusaurus.config.js — requires @docusaurus/preset-classic >= 3.5.0
    presets: [
      ['@docusaurus/preset-classic', {
        docs: {
          sidebarPath: require.resolve('./sidebars.js'),
          lastVersion: '1.0',            // 1.0 is the default served at /docs
          versions: {
            current: { label: 'Next (unreleased)', path: 'next' },
            '1.0': { label: 'v1.0', path: '1.0' },
          },
          onlyIncludeVersions: ['current', '1.0'],
        },
      }],
    ],
    
  3. Add the version dropdown to the navbar so readers can switch releases. The docsVersionDropdown item renders automatically from versions.json.

    // docusaurus.config.js — themeConfig.navbar.items
    themeConfig: {
      navbar: {
        items: [
          { type: 'docsVersionDropdown', position: 'right' },
        ],
      },
    },
    
  4. Cut subsequent versions on each release. Run the version command from CI when you tag a release so the snapshot is deterministic and reviewed.

    # On release tag v2.0
    npm run docusaurus docs:version 2.0
    
  5. Prune old versions to control build cost. Versioning multiplies your page count, which compounds the build-time concerns covered for large sites in Optimizing static site generation for 10k+ pages. Drop end-of-life versions from onlyIncludeVersions and delete their versioned_docs directory.

Validation

Verify four things in order: the snapshot and registry exist, the full site builds, each version path is emitted, and no cross-version links broke. Checking them in sequence isolates a versioning mistake to one stage.

Four-stage versioning validation Confirm the registry, build the site, check version routes, then scan for broken links. Registry versions.json Build all versions Routes /docs/1.0 · /next No broken links cross-version
Cross-version links are the most common breakage; the final scan is the one that catches it.
# 1. Confirm the version snapshot and registry exist
cat versions.json                 # Expected: ["1.0"] (then ["2.0","1.0"] later)
ls versioned_docs/                # Expected: version-1.0/ (and version-2.0/)

# 2. Build the full site including all versions
npm run build
# Expected: exit 0; "Success! Generated static files in 'build'."

# 3. Confirm versioned routes were emitted
ls build/docs/1.0/ && ls build/docs/next/
# Expected: HTML output present under each version path

# 4. Catch any broken cross-version links
npm run build 2>&1 | grep -iE "broken link" || echo "no broken links"
# Expected: "no broken links"

A healthy result: versions.json lists every active version, the build succeeds, and each version path renders under build/docs/.

Edge Cases & Troubleshooting

Versioning failures cluster around one misconception: that a frozen version is still editable in place. The diagram separates the symptoms that come from editing the wrong tree from those that come from build-cost neglect, so you fix the cause rather than the symptom.

Versioning symptoms grouped by cause Wrong-tree edits and build-cost neglect are the two causes behind the common versioning symptoms. Cause: edited wrong tree old version shows new content sidebar overwritten on re-cut Cause: build-cost neglect build time doubled prune old versions onlyIncludeVersions
Edit docs/ for the future and patch a frozen snapshot only deliberately; prune versions to keep builds fast.
Symptom Root Cause Resolution
Version dropdown missing from navbar docsVersionDropdown item not added Add the item to themeConfig.navbar.items
Old version shows new content Edited versioned_docs/version-X instead of docs/ Edit docs/ for unreleased changes; only patch snapshots intentionally
Build time doubled after versioning Every version rebuilt on each run Limit onlyIncludeVersions to actively supported releases
Broken links between versions Hardcoded absolute /docs/... paths Use relative links or the @site alias so links resolve per version
docs:version overwrote a sidebar Re-ran versioning for an existing version Versions are immutable; delete the snapshot before re-cutting

Frequently Asked Questions

How many versions should I keep live?

Keep the current/next version plus the releases you actively support — typically two or three. Each version is a full copy of the docs, so build time and bundle size grow linearly; archive end-of-life versions by removing them from onlyIncludeVersions.

Can I edit an already-released version’s docs?

Yes, by editing the files under versioned_docs/version-X/ directly, but treat that as a deliberate patch to a frozen release. Day-to-day edits belong in docs/, which represents the unreleased “next” version until you cut the next snapshot.