# How to Implement Versioning for Docusaurus Documentation: A Complete Guide

> Master Docusaurus versioning to create immutable documentation snapshots. Freeze released versions and manage ongoing development seamlessly with this complete guide.

- Repository: [Meta/docusaurus](https://github.com/facebook/docusaurus)
- Tags: how-to-guide
- Published: 2026-03-04

---

**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`](https://github.com/facebook/docusaurus/blob/main/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`](https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-plugin-content-docs/src/versions/version.ts). The `readVersionsMetadata` function validates your [`versions.json`](https://github.com/facebook/docusaurus/blob/main/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`](https://github.com/facebook/docusaurus/blob/main/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:

```bash
npm run docusaurus docs:version 1.2.0

```

This command performs three operations automatically:
- Copies the entire `docs/` directory to `versioned_docs/version-1.2.0/`
- Creates [`versioned_sidebars/version-1.2.0-sidebars.json`](https://github.com/facebook/docusaurus/blob/main/versioned_sidebars/version-1.2.0-sidebars.json) based on your current sidebar configuration
- Adds `"1.2.0"` to the [`versions.json`](https://github.com/facebook/docusaurus/blob/main/versions.json) file

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:

```javascript
// 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`](https://github.com/facebook/docusaurus/blob/main/website/src/pages/versions.tsx), the component consumes metadata through `useVersions` and `useLatestVersion` hooks:

```tsx
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:

```tsx
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`](https://github.com/facebook/docusaurus/blob/main/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`](https://github.com/facebook/docusaurus/blob/main/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:

```javascript
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`](https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-plugin-content-docs/src/versions/version.ts) (lines 49-73) validates your setup by checking:

1. That [`versions.json`](https://github.com/facebook/docusaurus/blob/main/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

- **Use the CLI** `npm run docusaurus docs:version [name]` to create immutable documentation snapshots in `versioned_docs/`
- **Configure routing** via `lastVersion` and `path` options in [`docusaurus.config.js`](https://github.com/facebook/docusaurus/blob/main/docusaurus.config.js) to control which version serves from `/docs`
- **Manage lifecycle states** using the `banner` property to mark versions as `'unreleased'` or `'unmaintained'`
- **Customize UI** by modifying [`website/src/pages/versions.tsx`](https://github.com/facebook/docusaurus/blob/main/website/src/pages/versions.tsx) and using the `useVersions` hook to access metadata
- **Understand the core** logic in [`packages/docusaurus-plugin-content-docs/src/versions/version.ts`](https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-plugin-content-docs/src/versions/version.ts) for advanced debugging

## 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`](https://github.com/facebook/docusaurus/blob/main/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`](https://github.com/facebook/docusaurus/blob/main/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`](https://github.com/facebook/docusaurus/blob/main/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`](https://github.com/facebook/docusaurus/blob/main/versions.json) file at your project root. These paths are read by the `readVersionsMetadata` function during the build process.