Skip to content

Versioning

Rspress provides powerful versioning capabilities, allowing you to maintain multiple documentation versions for different software releases. This chapter explores how to set up and manage versioned documentation sites.

What is Documentation Versioning?

Versioning Overview

Documentation versioning allows you to maintain separate documentation for each major version of your product:

  • Multiple Versions: Maintain docs for v1.0, v2.0, v3.0 simultaneously
  • Version Switching: Users can switch between versions
  • Version Archiving: Keep old versions for reference
  • Independent Content: Each version has its own content and configuration

Why Version Documentation?

User Scenarios:

  • Users may be on different software versions
  • Upgrades take time, users need access to old docs
  • API changes require different documentation
  • Features vary between versions

Maintenance Benefits:

  • Clear version history
  • Easier change tracking
  • Better user experience
  • Avoid confusion

Basic Version Setup

Directory Structure

Organize multi-version documentation:

docs/
├── v1/                     # Version 1.x docs
│   ├── index.md
│   ├── guide/
│   │   ├── getting-started.md
│   │   └── api.md
│   └── examples/
├── v2/                     # Version 2.x docs
│   ├── index.md
│   ├── guide/
│   │   ├── getting-started.md
│   │   ├── new-features.md
│   │   └── api.md
│   └── examples/
├── v3/                     # Version 3.x (latest)
│   ├── index.md
│   ├── guide/
│   │   ├── getting-started.md
│   │   ├── advanced.md
│   │   └── api.md
│   └── examples/
└── versions.json           # Version configuration

Version Configuration

Create versions.json:

json
{
  "latest": "v3",
  "versions": [
    {
      "version": "v3",
      "label": "v3.x (Latest)",
      "path": "/v3"
    },
    {
      "version": "v2",
      "label": "v2.x",
      "path": "/v2"
    },
    {
      "version": "v1",
      "label": "v1.x",
      "path": "/v1"
    }
  ]
}

Rspress Configuration

Configure versions in rspress.config.ts:

typescript
import { defineConfig } from 'rspress/config';
import versions from './versions.json';

export default defineConfig({
  title: 'My Documentation',

  // Version configuration
  themeConfig: {
    // Add version selector
    versioning: {
      latestVersion: versions.latest,
      versions: versions.versions,
    },

    // Configure navigation for each version
    nav: [
      { text: 'Home', link: '/' },
      { text: 'Guide', link: `/${versions.latest}/guide/` },
      { text: 'API', link: `/${versions.latest}/api/` },
      {
        text: `Version: ${versions.latest}`,
        items: versions.versions.map(v => ({
          text: v.label,
          link: v.path,
        })),
      },
    ],
  },
});

Version Selector

Built-in Version Selector

Rspress provides a built-in version selector component:

typescript
export default defineConfig({
  themeConfig: {
    // Enable version selector
    versioning: {
      enabled: true,
      latestVersion: 'v3',
      versions: [
        { version: 'v3', label: 'v3.x (Latest)' },
        { version: 'v2', label: 'v2.x' },
        { version: 'v1', label: 'v1.x (LTS)' },
      ],
      // Version selector position
      position: 'navbar', // or 'sidebar'
    },
  },
});

Custom Version Selector

Create a custom version switcher component:

tsx
// components/VersionSelector.tsx
import React from 'react';
import { useLocation } from 'rspress/runtime';
import versions from '../versions.json';

export function VersionSelector() {
  const location = useLocation();
  const currentVersion = location.pathname.split('/')[1] || versions.latest;

  const handleVersionChange = (version: string) => {
    const currentPath = location.pathname.replace(`/${currentVersion}`, `/${version}`);
    window.location.href = currentPath;
  };

  return (
    <div className="version-selector">
      <label htmlFor="version-select">Version:</label>
      <select
        id="version-select"
        value={currentVersion}
        onChange={(e) => handleVersionChange(e.target.value)}
        className="version-dropdown"
      >
        {versions.versions.map((v) => (
          <option key={v.version} value={v.version}>
            {v.label}
          </option>
        ))}
      </select>
    </div>
  );
}

Style the version selector:

css
/* styles/version-selector.css */
.version-selector {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem;
}

.version-selector label {
  font-size: 0.875rem;
  color: var(--rp-c-text-2);
}

.version-dropdown {
  padding: 0.375rem 0.75rem;
  border: 1px solid var(--rp-c-border);
  border-radius: 6px;
  background: var(--rp-c-bg);
  color: var(--rp-c-text-1);
  font-size: 0.875rem;
  cursor: pointer;
  transition: all 0.3s;
}

.version-dropdown:hover {
  border-color: var(--rp-c-brand);
}

.version-dropdown:focus {
  outline: none;
  border-color: var(--rp-c-brand);
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}

Version Banners

Outdated Version Warning

Add warning banners for old versions:

tsx
// components/VersionBanner.tsx
import React from 'react';
import { useLocation } from 'rspress/runtime';
import versions from '../versions.json';

export function VersionBanner() {
  const location = useLocation();
  const currentVersion = location.pathname.split('/')[1];
  const isLatest = currentVersion === versions.latest;

  if (isLatest) {
    return null;
  }

  const latestVersionInfo = versions.versions.find(
    v => v.version === versions.latest
  );

  return (
    <div className="version-banner warning">
      <div className="banner-content">
        <span className="banner-icon">⚠️</span>
        <span className="banner-text">
          You are viewing docs for <strong>{currentVersion}</strong>.
          The latest version is{' '}
          <a href={latestVersionInfo?.path}>
            {latestVersionInfo?.label}
          </a>
          .
        </span>
      </div>
    </div>
  );
}

Style the banner:

css
/* styles/version-banner.css */
.version-banner {
  padding: 1rem;
  margin-bottom: 2rem;
  border-radius: 8px;
  background: #fff3cd;
  border: 1px solid #ffc107;
  color: #856404;
}

.version-banner.warning {
  background: #fff3cd;
  border-color: #ffc107;
  color: #856404;
}

.version-banner.info {
  background: #d1ecf1;
  border-color: #17a2b8;
  color: #0c5460;
}

.banner-content {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.banner-icon {
  font-size: 1.5rem;
  flex-shrink: 0;
}

.banner-text {
  line-height: 1.6;
}

.banner-text a {
  color: inherit;
  text-decoration: underline;
  font-weight: 600;
}

.banner-text a:hover {
  opacity: 0.8;
}

Version Navigation

Per-Version Sidebars

Configure different sidebars for different versions:

typescript
export default defineConfig({
  themeConfig: {
    sidebar: {
      // v3 sidebar
      '/v3/': [
        {
          text: 'Getting Started',
          items: [
            { text: 'Introduction', link: '/v3/guide/introduction' },
            { text: 'Quick Start', link: '/v3/guide/getting-started' },
            { text: 'New Features', link: '/v3/guide/new-features' },
          ],
        },
        {
          text: 'API',
          items: [
            { text: 'API Reference', link: '/v3/api/reference' },
            { text: 'Advanced Usage', link: '/v3/api/advanced' },
          ],
        },
      ],

      // v2 sidebar
      '/v2/': [
        {
          text: 'Getting Started',
          items: [
            { text: 'Introduction', link: '/v2/guide/introduction' },
            { text: 'Quick Start', link: '/v2/guide/getting-started' },
          ],
        },
        {
          text: 'API',
          items: [
            { text: 'API Reference', link: '/v2/api/reference' },
          ],
        },
      ],

      // v1 sidebar
      '/v1/': [
        {
          text: 'Documentation',
          items: [
            { text: 'Introduction', link: '/v1/guide/introduction' },
            { text: 'API', link: '/v1/api/reference' },
          ],
        },
      ],
    },
  },
});

Maintain correct links within each version:

markdown
<!-- v3/guide/introduction.md -->
---
title: Introduction
---

# Introduction to v3

Welcome to version 3!

## Next Steps

- [Quick Start](./getting-started.md)
- [New Features](./new-features.md)
- [API Reference](/v3/api/reference)

## Migrating from v2

See our [Migration Guide](./migration-from-v2.md).

Version Management Workflow

Creating a New Version

When releasing a new major version:

  1. Copy Current Docs
bash
# Copy v3 to v4
cp -r docs/v3 docs/v4

# Mark v3 as old version
# Update v4 content with new features
  1. Update Version Config
json
{
  "latest": "v4",
  "versions": [
    {
      "version": "v4",
      "label": "v4.x (Latest)",
      "path": "/v4"
    },
    {
      "version": "v3",
      "label": "v3.x",
      "path": "/v3"
    },
    {
      "version": "v2",
      "label": "v2.x (Archived)",
      "path": "/v2"
    }
  ]
}
  1. Update Config File
typescript
export default defineConfig({
  themeConfig: {
    versioning: {
      latestVersion: 'v4',
      // ...update version list
    },
  },
});

Maintaining Multiple Versions

Best Practices:

  1. Major Updates: Only update latest version
  2. Critical Fixes: Backport to supported versions
  3. Security Updates: Update all active versions
  4. Archiving: Mark very old versions as archived
bash
# Example workflow
git checkout main           # Latest version (v4)
# Make changes...

git checkout v3-docs        # Supported version
# Backport important fixes...

git checkout v2-archived    # Critical security fixes only

Long-Term Support (LTS)

Marking LTS Versions

Indicate which versions are LTS:

json
{
  "latest": "v4",
  "versions": [
    {
      "version": "v4",
      "label": "v4.x (Latest)",
      "path": "/v4",
      "lts": false
    },
    {
      "version": "v3",
      "label": "v3.x (LTS)",
      "path": "/v3",
      "lts": true,
      "endOfLife": "2025-12-31"
    },
    {
      "version": "v2",
      "label": "v2.x (EOL)",
      "path": "/v2",
      "lts": false,
      "endOfLife": "2024-06-30"
    }
  ]
}

LTS Banner

Display LTS information:

tsx
export function LTSBanner({ version }) {
  const versionInfo = versions.versions.find(v => v.version === version);

  if (!versionInfo) return null;

  if (versionInfo.lts) {
    return (
      <div className="version-banner info">
        <span className="banner-icon">✅</span>
        <span className="banner-text">
          This version is LTS (Long Term Support). Supported until{' '}
          {new Date(versionInfo.endOfLife).toLocaleDateString()}.
        </span>
      </div>
    );
  }

  if (versionInfo.endOfLife && new Date(versionInfo.endOfLife) < new Date()) {
    return (
      <div className="version-banner warning">
        <span className="banner-icon">⚠️</span>
        <span className="banner-text">
          This version has reached End of Life (EOL) and no longer receives updates.
        </span>
      </div>
    );
  }

  return null;
}

Version-Specific Features

Feature Flags

Show content based on version:

mdx
import { VersionFeature } from '../components/VersionFeature';

# API Reference

<VersionFeature minVersion="v3">

## New Async API

v3 introduces a new Promise-based API:

`````javascript
// Available in v3+
const result = await api.fetchData();
```

</VersionFeature>

<VersionFeature maxVersion="v2">

## Legacy Callback API

v2 and earlier use callbacks:

```javascript
// v2 and earlier
api.fetchData((error, result) => {
  if (error) {
    console.error(error);
  }
});
```

</VersionFeature>
```

Implement VersionFeature component:

```tsx
// components/VersionFeature.tsx
import React from 'react';
import { useLocation } from 'rspress/runtime';

interface VersionFeatureProps {
  minVersion?: string;
  maxVersion?: string;
  children: React.ReactNode;
}

export function VersionFeature({
  minVersion,
  maxVersion,
  children,
}: VersionFeatureProps) {
  const location = useLocation();
  const currentVersion = location.pathname.split('/')[1];

  // Simple version comparison
  const versionNumber = (v: string) => parseInt(v.replace('v', ''), 10);
  const current = versionNumber(currentVersion);

  if (minVersion && current < versionNumber(minVersion)) {
    return null;
  }

  if (maxVersion && current > versionNumber(maxVersion)) {
    return null;
  }

  return <>{children}</>;
}
```

## Version Redirects

### Auto-Redirect

Redirect root path to latest version:

```typescript
export default defineConfig({
  builderConfig: {
    html: {
      redirects: [
        {
          from: '/',
          to: '/v4/',
        },
        {
          from: '/guide',
          to: '/v4/guide/',
        },
      ],
    },
  },
});
```

### Smart Version Detection

Redirect based on user context:

```tsx
// components/VersionRedirect.tsx
import { useEffect } from 'react';
import { useLocation } from 'rspress/runtime';

export function VersionRedirect() {
  const location = useLocation();

  useEffect(() => {
    // Check if accessing an archived version
    const currentVersion = location.pathname.split('/')[1];
    const savedVersion = localStorage.getItem('preferredVersion');

    // Redirect if user has a preferred version
    if (savedVersion && savedVersion !== currentVersion) {
      const newPath = location.pathname.replace(
        `/${currentVersion}`,
        `/${savedVersion}`
      );
      window.location.href = newPath;
    }
  }, [location]);

  return null;
}
```

## Version Search

### Per-Version Search

Configure version-specific search:

```typescript
export default defineConfig({
  themeConfig: {
    search: {
      versioned: true,
      // Create separate search index for each version
      indexes: [
        { version: 'v4', path: '/v4' },
        { version: 'v3', path: '/v3' },
        { version: 'v2', path: '/v2' },
      ],
    },
  },
});
```

## Best Practices

### Version Naming

1. **Semantic Versioning**: Use v1.x, v2.x, v3.x
2. **Clear Labels**: "v3.x (Latest)", "v2.x (LTS)", "v1.x (Archived)"
3. **Consistency**: Use same naming across all docs

### Content Management

1. **Keep in Sync**: Regularly update all supported versions
2. **Clear Migration Guides**: Provide migration paths between versions
3. **Highlight Changes**: Mark new features in each version
4. **Archive Old Versions**: Clearly mark EOL versions

### User Experience

1. **Default to Latest**: New users should see latest docs
2. **Preserve User Choice**: Remember user's selected version
3. **Clear Visual Cues**: Show current version prominently
4. **Easy Switching**: Make version switching effortless

## Automation

### Version Release Script

```bash
#!/bin/bash
# scripts/release-version.sh

NEW_VERSION=$1
CURRENT_VERSION=$(jq -r '.latest' versions.json)

if [ -z "$NEW_VERSION" ]; then
  echo "Usage: ./release-version.sh v4"
  exit 1
fi

echo "Creating new version: $NEW_VERSION"

# Copy current version
cp -r "docs/$CURRENT_VERSION" "docs/$NEW_VERSION"

# Update versions.json
jq ".latest = \"$NEW_VERSION\"" versions.json > versions.tmp.json
jq ".versions |= [{
  version: \"$NEW_VERSION\",
  label: \"$NEW_VERSION.x (Latest)\",
  path: \"/$NEW_VERSION\"
}] + ." versions.tmp.json > versions.json

rm versions.tmp.json

echo "✅ Version $NEW_VERSION created"
echo "Now update content in docs/$NEW_VERSION"
```

### Version Archive Script

```bash
#!/bin/bash
# scripts/archive-version.sh

VERSION=$1

if [ -z "$VERSION" ]; then
  echo "Usage: ./archive-version.sh v2"
  exit 1
fi

echo "Archiving version: $VERSION"

# Update version label
jq "(.versions[] | select(.version == \"$VERSION\") | .label) |= . + \" (Archived)\"" \
  versions.json > versions.tmp.json

mv versions.tmp.json versions.json

echo "✅ Version $VERSION archived"
```

## Troubleshooting

### Broken Version Links

**Issue**: Links point to wrong version

**Solution**:
```markdown
<!-- ✅ Use absolute paths -->
[API Reference](/v3/api/reference)

<!-- ❌ Avoid relative paths across versions -->
[API Reference](../../v2/api/reference)
```

### Version Selector Not Showing

**Issue**: Version selector doesn't appear

**Check**:
1. `versions.json` is properly formatted
2. Versioning enabled in config
3. At least 2 versions configured

### Search Shows All Versions

**Issue**: Search results mix all versions

**Solution**:
```typescript
export default defineConfig({
  themeConfig: {
    search: {
      versioned: true, // Enable versioned search
    },
  },
});
```

---

## Next Steps

- Explore [Built-in Components](./built-in-components.md)
- Learn about [Build Extensions](./build-extensions.md)
- Read about [Custom Search](./custom-search.md)

---

::: tip Best Practices
- Always maintain at least one LTS version
- Provide clear migration guides for each version
- Automate version creation process
- Keep version URL structure consistent
- Add clear warning banners for old versions
:::

::: warning Common Pitfalls
- Don't forget to update internal links
- Don't mix content between versions
- Don't maintain all versions indefinitely
- Don't use ambiguous version labels
- Don't forget to update search indexes
:::

::: info Resources
- [Semantic Versioning](https://semver.org/)
- [Documentation Versioning Best Practices](https://www.writethedocs.org/)
- [Rspress Configuration Reference](https://rspress.dev/api/config)
:::

Content is for learning and research only.