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/core3.5.0+). - The classic preset configured with a
docsinstance and a workingsidebars.js. - A Git repository with a branching/tagging convention for releases.
- Current, unversioned content under
docs/that builds cleanly withnpm run build.
# Requires @docusaurus/core >= 3.5.0
npx docusaurus --version # 3.5.x
Exact Configuration
-
Cut your first version snapshot. This copies the current
docs/intoversioned_docs/version-1.0/, snapshots the sidebar, and appends the label toversions.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 -
Configure how versions are labeled and routed in
docusaurus.config.js. SettinglastVersionand an explicitversionsmap 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'], }, }], ], -
Add the version dropdown to the navbar so readers can switch releases. The
docsVersionDropdownitem renders automatically fromversions.json.// docusaurus.config.js — themeConfig.navbar.items themeConfig: { navbar: { items: [ { type: 'docsVersionDropdown', position: 'right' }, ], }, }, -
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 -
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
onlyIncludeVersionsand delete theirversioned_docsdirectory.
Validation
# 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
| 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.