---
title: Versioned docs
description: Configure multiple documentation versions with package-backed source helpers
type: guide
summary: Use createVersionedSources to configure stable, pre-release, or host-based documentation versions.
prerequisites:
  - /docs/configuration
related:
  - /docs/proxy
  - /docs/llms-txt
---

# Versioned docs



Versioned docs let you serve multiple documentation sets from one Geistdocs project. Use them for stable and pre-release docs, previous major versions, or host-based version switching.

<CopyPrompt text="Help me add versioned docs to this Geistdocs site. Inspect source.config.ts, lib/geistdocs/source.ts, app route files, and the docs layout, then propose a versioned source setup using createVersionedSources.">
  Help me add versioned docs to this Geistdocs site. Inspect `source.config.ts`, `lib/geistdocs/source.ts`, app route files, and the docs layout, then propose a versioned source setup using `createVersionedSources`.
</CopyPrompt>

## Configure source collections

Create one Fumadocs collection for each version in `source.config.ts`:

```ts title="source.config.ts"
import {
  defineGeistdocsSourceConfig,
  geistdocsFrontmatterSchema,
} from "@vercel/geistdocs/source-config";
import { defineDocs } from "fumadocs-mdx/config";

export const v4docs = defineDocs({
  dir: "content/docs/v4",
  docs: {
    schema: geistdocsFrontmatterSchema,
    postprocess: {
      includeProcessedMarkdown: true,
    },
  },
});

export const v5docs = defineDocs({
  dir: "content/docs/v5",
  docs: {
    schema: geistdocsFrontmatterSchema,
    postprocess: {
      includeProcessedMarkdown: true,
    },
  },
});

export default defineGeistdocsSourceConfig();
```

## Create versioned sources

Use `createVersionedSources` in `lib/geistdocs/source.ts`:

```ts title="lib/geistdocs/source.ts"
import { createVersionedSources } from "@vercel/geistdocs/source";
import { v4docs, v5docs } from "@/.source/server";
import { config } from "./config";

export const versions = createVersionedSources({
  config,
  current: "v4",
  versions: [
    {
      id: "v4",
      label: "v4",
      docs: v4docs,
      baseUrl: "/docs",
    },
    {
      id: "v5",
      label: "v5 pre-release",
      docs: v5docs,
      baseUrl: "/docs",
      routePrefix: "/v5",
    },
  ],
});

export const geistdocsSource = versions.current;
export const source = geistdocsSource.source;
```

The returned object includes:

| Property         | Type                                    | Description                                           |
| ---------------- | --------------------------------------- | ----------------------------------------------------- |
| `current`        | `GeistdocsSourceBundle`                 | The source matching the `current` version ID.         |
| `byId`           | `Record<string, GeistdocsSourceBundle>` | A map of version IDs to source bundles.               |
| `all`            | `Array`                                 | Version metadata and source bundles for rendering UI. |
| `currentVersion` | `string`                                | The active version ID.                                |

## Render versioned pages

Use the current version for `/docs` and a specific version for version-prefixed routes:

```tsx title="app/[lang]/docs/[[...slug]]/page.tsx"
import { createDocsPage } from "@vercel/geistdocs/pages/docs";
import { config } from "@/lib/geistdocs/config";
import { versions } from "@/lib/geistdocs/source";

const docsPage = createDocsPage({
  config,
  source: versions.current,
});

export default docsPage.Page;
export const generateStaticParams = docsPage.generateStaticParams;
export const generateMetadata = docsPage.generateMetadata;
```

```tsx title="app/[lang]/v5/docs/[[...slug]]/page.tsx"
import { createDocsPage } from "@vercel/geistdocs/pages/docs";
import { config } from "@/lib/geistdocs/config";
import { versions } from "@/lib/geistdocs/source";

const docsPage = createDocsPage({
  config,
  source: versions.byId.v5,
  getPageUrl: ({ page }) => `/v5${page.url}`,
  metadata: ({ metadata }) => ({
    ...metadata,
    robots: {
      index: false,
      follow: true,
    },
  }),
});

export default docsPage.Page;
export const generateStaticParams = docsPage.generateStaticParams;
export const generateMetadata = docsPage.generateMetadata;
```

Use `getPageUrl` when the route path differs from the source `baseUrl`. This keeps page actions and markdown links pointed at the public URL.

## Add a version selector

Use `GeistdocsVersionSelect` when readers need to move between versions:

```tsx title="components/geistdocs/docs-layout.tsx"
import { GeistdocsDocsLayout } from "@vercel/geistdocs/layout";
import { GeistdocsVersionSelect } from "@vercel/geistdocs/versions";
import { config } from "@/lib/geistdocs/config";

export const DocsLayout = ({ tree, children }: DocsLayoutProps) => (
  <GeistdocsDocsLayout
    config={config}
    sidebarTop={
      <GeistdocsVersionSelect
        current="v4"
        versions={[
          { id: "v4", label: "v4" },
          { id: "v5", label: "v5 pre-release", routePrefix: "/v5" },
        ]}
      />
    }
    tree={tree}
  >
    {children}
  </GeistdocsDocsLayout>
);
```

Each version can define an `icon` to override the default current/previous version icons:

```tsx
<GeistdocsVersionSelect
  current="v6"
  versions={[
    { id: "v6", label: "v6 latest", icon: <span>6</span> },
    { id: "v5", label: "v5", icon: <span>5</span>, routePrefix: "/v5" },
  ]}
/>
```

Use `GeistdocsRouteSelect` for framework, product, or route switchers that should not use version-specific labels:

```tsx
import { LogoNextjs, LogoSvelteKit } from "@vercel/geistdocs/assets/logos";
import { GeistdocsRouteSelect } from "@vercel/geistdocs/versions";

<GeistdocsRouteSelect
  ariaLabel="Select framework"
  current="nextjs"
  items={[
    {
      id: "nextjs",
      label: "Next.js",
      description: "React framework",
      icon: <LogoNextjs height={18} />,
      href: "/:path*",
    },
    {
      id: "sveltekit",
      label: "SvelteKit",
      description: "Svelte framework",
      icon: <LogoSvelteKit height={18} />,
      routePrefix: "/sveltekit",
      href: "/sveltekit/:path*",
    },
  ]}
/>
```

For host-based versions, use `href` instead of `routePrefix`:

```tsx
<GeistdocsVersionSelect
  current="v6"
  versions={[
    { id: "v6", label: "v6 latest" },
    { id: "v5", label: "v5", href: "https://v5.example.com/:path*" },
    { id: "v4", label: "v4", href: "https://v4.example.com/:path*" },
  ]}
/>
```

## Configure markdown routes

Each public route family needs a matching markdown route in `proxy.ts`. Read [Proxy and markdown routes](/docs/proxy) for the proxy configuration.

## Next steps

* Read [Proxy and markdown routes](/docs/proxy) to connect versioned pages to `.md`, `.mdx`, and AI-agent rewrites.
* Read [llms.txt](/docs/llms-txt) to configure all-docs markdown output for multiple sources.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)