How to Implement Versioning for Docusaurus Documentation: A Complete Guide

Docusaurus provides a built-in versioning system that creates immutable snapshots of your documentation, allowing you to freeze released versions while maintaining a "next" version for ongoing development.

Implementing versioning for Docusaurus documentation requires understanding the CLI tools, configuration options in docusaurus.config.js, and the internal metadata system that powers the versions UI. The facebook/docusaurus repository contains a robust versioning core within the docs plugin that handles path generation, banner display, and sidebar management automatically.

How Docusaurus Versioning Works

The versioning system in facebook/docusaurus operates through three coordinated mechanisms: the CLI for snapshotting documentation, the plugin configuration for customizing behavior, and the React components for rendering the versions UI.

At the core, the system relies on functions in packages/docusaurus-plugin-content-docs/src/versions/version.ts. The readVersionsMetadata function validates your versions.json file and builds metadata objects, while createVersionMetadata computes the URL paths, edit links, and banner settings for each version. The getVersionPathPart function specifically determines whether a version serves from /, /next, or a version-specific subdirectory like /1.2.0.

When you create a version, Docusaurus:

  1. Copies the current docs/ folder to versioned_docs/version-[name]/
  2. Generates a sidebar snapshot at versioned_sidebars/version-[name]-sidebars.json
  3. Appends the version name to versions.json

Step-by-Step Implementation

1. Create a New Version Using the CLI

To freeze your current documentation as a new version, run the Docusaurus CLI command from your project root:

npm run docusaurus docs:version 1.2.0

This command performs three operations automatically:

After running this command, your file structure will include:


project-root/
├── docs/                          # Next (unreleased) documentation

├── versioned_docs/
│   └── version-1.2.0/            # Frozen snapshot

├── versioned_sidebars/
│   └── version-1.2.0-sidebars.json
└── versions.json                 # ["1.2.0"]

Source reference: Official versioning guide at website/docs/guides/docs/versioning.mdx lines 96-110.

2. Configure Version Behavior in docusaurus.config.js

Add the docs configuration object to your preset settings to control routing, labels, and banners. This example configures version 1.2.0 as the default while maintaining a "Next" version for unreleased features:

// docusaurus.config.js
module.exports = {
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        docs: {
          // Routes /docs to this version
          lastVersion: '1.2.0',
          
          // Include the ./docs folder as a "next" version
          includeCurrentVersion: true,
          
          // Per-version customization
          versions: {
            current: {
              label: 'Next',
              path: 'next',
              banner: 'unreleased',
            },
            '1.2.0': {
              label: '1.2.0 (stable)',
              path: '',
              banner: 'none',
            },
            '1.1.0': {
              label: '1.1.0',
              banner: 'unmaintained',
            },
          },
        },
      },
    ],
  ],
};

Key configuration options:

  • lastVersion: Defines which version serves at the /docs base path
  • includeCurrentVersion: Boolean to include or hide the ./docs folder (the "next" version)
  • versions: Object mapping version names to their display properties including label, path, banner, and badge

3. Customize the Versions Page UI

The default versions page (/versions) uses React hooks to display available documentation versions. In website/src/pages/versions.tsx, the component consumes metadata through useVersions and useLatestVersion hooks:

import {useVersions, useLatestVersion} from '@theme/hooks/useDocs';

function VersionsPage() {
  const versions = useVersions('default');
  const latestVersion = useLatestVersion('default');
  
  return (
    <table>
      <tbody>
        {versions.map(version => (
          <tr key={version.name}>
            <th>{version.label}</th>
            <td>
              <Link to={version.path}>Documentation</Link>
            </td>
            <td>
              <Link to={`${REPO_URL}/releases/tag/v${version.name}`}>
                Release Notes
              </Link>
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

To customize release note URLs for specific versions, modify the URL generation logic:

function getReleaseNotesUrl(version) {
  if (version.name === '2.x') {
    return 'https://github.com/facebook/docusaurus/blob/main/CHANGELOG-v2.md';
  }
  return `${REPO_URL}/releases/tag/v${version.name}`;
}

Source file: website/src/pages/versions.tsx

Versioning Configuration Options

The docusaurus-plugin-content-docs package exposes granular controls for each version. When the createVersionMetadata function processes your configuration (source: packages/docusaurus-plugin-content-docs/src/versions/version.ts lines 81-115), it evaluates these properties:

  • path: The URL segment (e.g., next, 1.0, or empty string for root)
  • label: The display name in the version dropdown
  • banner: Accepts 'unreleased', 'unmaintained', or 'none' to display informational banners
  • badge: Boolean to show a version badge on each page
  • className: Custom CSS class added to the document container

Example configuration for a legacy version with warnings:

versions: {
  '1.0.0': {
    label: '1.0.0 (Legacy)',
    path: '1.0',
    banner: 'unmaintained',
    badge: true,
  }
}

Versioning Internals and File Structure

Understanding the internal file structure helps troubleshoot versioning issues. The readVersionsMetadata function in packages/docusaurus-plugin-content-docs/src/versions/version.ts (lines 49-73) validates your setup by checking:

  1. That versions.json exists and contains valid version names
  2. That corresponding folders exist in versioned_docs/
  3. That sidebar files exist in versioned_sidebars/

The system generates routes dynamically based on the getVersionPathPart logic (lines 66-78), which constructs URL patterns like:

  • /docs/ for the version specified in lastVersion
  • /docs/next/ for the current version (when configured)
  • /docs/1.2.0/ for specific versioned paths

Summary

Frequently Asked Questions

How do I create a new version in Docusaurus?

Run the CLI command npm run docusaurus docs:version 1.0.0 from your project root. This copies your current docs/ folder to versioned_docs/version-1.0.0/, creates a corresponding sidebar file in versioned_sidebars/, and updates versions.json. The version immediately becomes available at /docs/1.0.0/ unless you customize the path in your configuration.

How do I hide the "next" (unreleased) version from my site?

Set includeCurrentVersion: false in your docs configuration within docusaurus.config.js. This prevents Docusaurus from serving the ./docs folder as a version, effectively hiding your work-in-progress documentation from readers while keeping it available in your repository.

How do I mark a version as unmaintained in Docusaurus?

Add the banner: 'unmaintained' property to the version configuration in docusaurus.config.js. This displays a warning banner at the top of every page in that version, informing users that the documentation is no longer receiving updates. You can also use banner: 'unreleased' for versions that are newer than your stable release.

Where does Docusaurus store versioned documentation files?

Docusaurus stores versioned documentation in three locations: the markdown files reside in versioned_docs/version-[name]/, the sidebar configurations are in versioned_sidebars/version-[name]-sidebars.json, and the version registry is maintained in the versions.json file at your project root. These paths are read by the readVersionsMetadata function during the build process.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →