This guide walks you through migrating an existing shipyard documentation site to use versioned documentation. The migration is non-breaking and can be done incrementally.
Before migrating, ensure you have:
@levino/shipyard-docs version 0.4.0 or later installedThe migration process involves:
Important: This migration is non-breaking. Your existing URLs will continue to work, and the version system adds new URL patterns rather than changing existing ones.
Before restructuring content, decide on your versioning approach:
How many versions will you maintain?
What version naming scheme will you use?
v1, v2, v3v1, v2 (avoid dots in folder names)stable, next, legacyDo you need to keep the current docs as-is?
v1, and you create v2 for changesFor most migrations, we recommend:
v1)v2) only when you have breaking changesYour existing structure likely looks like this:
docs/
├── index.md
├── getting-started.md
├── configuration.md
└── advanced/
├── api.md
└── plugins.md
Or with i18n:
docs/
├── en/
│ ├── index.md
│ └── getting-started.md
└── de/
├── index.md
└── getting-started.md
Move all content into a version directory:
Without i18n:
docs/
└── v1/
├── index.md
├── getting-started.md
├── configuration.md
└── advanced/
├── api.md
└── plugins.md
With i18n:
docs/
└── v1/
├── en/
│ ├── index.md
│ └── getting-started.md
└── de/
├── index.md
└── getting-started.md
Run these commands from your project root:
Without i18n:
# Create version directory
mkdir -p docs/v1
# Move all content into version directory
mv docs/*.md docs/v1/
mv docs/*/ docs/v1/ 2>/dev/null || true
# Verify structure
ls -la docs/v1/
With i18n:
# Create version directory
mkdir -p docs/v1
# Move locale directories into version directory
mv docs/en docs/v1/
mv docs/de docs/v1/
# Repeat for other locales
# Verify structure
ls -la docs/v1/
Update your src/content.config.ts (or src/content/config.ts):
import { defineCollection } from 'astro:content'
import { docsSchema } from '@levino/shipyard-docs'
import { glob } from 'astro/loaders'
const docs = defineCollection({
loader: glob({ pattern: '**/*.md', base: './docs' }),
schema: docsSchema,
})
export const collections = { docs }
import { defineCollection } from 'astro:content'
import { createVersionedDocsCollection } from '@levino/shipyard-docs'
const docs = defineCollection(
createVersionedDocsCollection('./docs', {
versions: ['v1'], // Add more versions as needed
fallbackVersion: 'v1',
}),
)
export const collections = { docs }
createVersionedDocsCollection instead of docsSchema and globversions arrayUpdate your astro.config.mjs:
import { defineConfig } from 'astro/config'
import shipyardDocs from '@levino/shipyard-docs'
export default defineConfig({
integrations: [
shipyardDocs({
// existing config
}),
],
})
import { defineConfig } from 'astro/config'
import shipyardDocs from '@levino/shipyard-docs'
export default defineConfig({
integrations: [
shipyardDocs({
// existing config
versions: {
current: 'v1',
available: [
{ version: 'v1', label: 'Version 1.0' },
],
stable: 'v1',
},
}),
],
})
| Property | Description | Initial Value |
|---|---|---|
current | Default version for /docs/ | Your version name |
available | Array of version configs | Single version initially |
deprecated | Versions to show warnings for | Empty array |
stable | Marked as stable release | Same as current |
# Build the site
npm run build
# Preview locally
npm run preview
After migration, your URLs will work as follows:
| Old URL | New URL | Notes |
|---|---|---|
/docs/getting-started | /docs/v1/getting-started | Version prefix added |
/docs/ | /docs/v1/ | Redirects to current version |
| N/A | /docs/latest/getting-started | New alias for current |
With i18n:
| Old URL | New URL |
|---|---|
/en/docs/getting-started | /en/docs/v1/getting-started |
/de/docs/getting-started | /de/docs/v1/getting-started |
If you have existing E2E tests, update URL expectations:
// Before
await page.goto('/docs/getting-started')
// After
await page.goto('/docs/v1/getting-started')
// Or use latest alias:
await page.goto('/docs/latest/getting-started')
If your documentation has internal links, update them to include the version:
Relative links automatically work within the same version:
<!-- This works - stays within same version -->
See the [configuration guide](./configuration)
See the [API reference](../advanced/api)
For links that should always point to the current version:
<!-- Always points to current version -->
See the [getting started](/docs/latest/getting-started)
For links to specific versions:
<!-- Links to specific version -->
See the [v1 migration guide](/docs/v1/migration)
When you’re ready to add a new version:
cp -r docs/v1 docs/v2
// astro.config.mjs
versions: {
current: 'v2', // Update current
available: [
{ version: 'v2', label: 'Version 2.0 (Latest)' },
{ version: 'v1', label: 'Version 1.0' },
],
stable: 'v2', // Update stable
},
createVersionedDocsCollection('./docs', {
versions: ['v1', 'v2'], // Add new version
fallbackVersion: 'v2', // Update fallback
})
Modify the content in docs/v2/ to reflect the new version’s changes.
Build fails with “cannot find content”
versions array includes all your version folder namesVersion selector doesn’t appear
404 errors for existing URLs
Sidebar shows wrong pages
If you encounter issues:
rm -rf dist .astro && npm run buildIf you need to revert the migration:
Move content back out of the version directory:
mv docs/v1/* docs/
rmdir docs/v1
Revert src/content.config.ts to use docsSchema and glob directly
Remove the versions config from astro.config.mjs