The docs package provides documentation features for shipyard including automatic sidebar generation, pagination between pages, and git metadata display.
npm install @levino/shipyard-docs
Requires @levino/shipyard-base to be installed and configured.
For Tailwind to correctly pick up the classes used in shipyard-docs components, you need to add the package path to the content array in your tailwind.config.mjs:
const path = require('node:path')
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
// Include shipyard-base (required)
path.join(
path.dirname(require.resolve('@levino/shipyard-base')),
'../astro/**/*.astro',
),
// Include shipyard-docs
path.join(
path.dirname(require.resolve('@levino/shipyard-docs')),
'../astro/**/*.astro',
),
],
// ... rest of your config
}
import shipyard from '@levino/shipyard-base'
import shipyardDocs from '@levino/shipyard-docs'
import { defineConfig } from 'astro/config'
export default defineConfig({
integrations: [
shipyard({
brand: 'My Site',
title: 'My Site',
tagline: 'Documentation',
navigation: {
docs: { label: 'Docs', href: '/docs' },
},
}),
shipyardDocs(),
],
})
Create src/content.config.ts:
import { defineCollection } from 'astro:content'
import { createDocsCollection } from '@levino/shipyard-docs'
const docs = defineCollection(createDocsCollection('./docs'))
export const collections = { docs }
Add markdown files to the docs/ directory:
---
title: Getting Started
sidebar:
position: 1
---
# Getting Started
Your documentation content...
That’s it! Your documentation is now available at /docs.
| Option | Type | Default | Description |
|---|---|---|---|
routeBasePath | string | 'docs' | Base URL path for documentation |
collectionName | string | Same as routeBasePath | Name of the content collection |
editUrl | string | — | Base URL for “Edit this page” links |
showLastUpdateTime | boolean | false | Show last update timestamp from git |
showLastUpdateAuthor | boolean | false | Show last update author from git |
versions | VersionConfig | — | Enable multi-version documentation (see Versioning) |
llmsTxt | LlmsTxtConfig | — | Enable llms.txt generation (see LLMs.txt) |
prerender | boolean | true | Prerender docs at build time. Set to false for SSR sites with auth middleware |
shipyardDocs({
routeBasePath: 'docs',
editUrl: 'https://github.com/user/repo/edit/main/docs',
showLastUpdateTime: true,
showLastUpdateAuthor: true,
})
If you’re running an SSR site with authentication middleware that needs access to request headers or cookies, you may need to disable prerendering:
shipyardDocs({
routeBasePath: 'docs',
prerender: false, // Required for SSR sites with auth middleware
})
When prerender: false, docs pages are rendered on-demand and have full access to Astro.request.headers and cookies.
| Field | Type | Description |
|---|---|---|
title | string | Page title |
description | string | Page description for SEO |
| Field | Type | Default | Description |
|---|---|---|---|
sidebar.position | number | Infinity | Position in sidebar (lower = earlier) |
sidebar.label | string | title | Custom label for sidebar |
sidebar.render | boolean | true | Whether to show in sidebar |
| Field | Type | Default | Description |
|---|---|---|---|
pagination_next | string | null | Auto | Next page doc ID, or null to disable |
pagination_prev | string | null | Auto | Previous page doc ID, or null to disable |
| Field | Type | Description |
|---|---|---|
last_update_author | string | false | Override author, or false to hide |
last_update_time | Date | false | Override timestamp, or false to hide |
custom_edit_url | string | null | Custom edit URL, or null to disable |
The sidebar is automatically generated from your docs folder structure.
| Concept | Behavior |
|---|---|
| Folders | Become collapsible categories in the sidebar |
index.md | Acts as the category landing page; its title becomes the category label |
| Files | Appear as entries under their parent folder/category |
| Ordering | Controlled by sidebar.position (lower = earlier); defaults to Infinity (end) |
| Labels | Use sidebar.label to override the display name; defaults to title |
docs/
├── index.md # → Docs landing page
├── getting-started.md # → Top-level entry
├── guides/
│ ├── index.md # → "Guides" category (uses title from this file)
│ ├── installation.md # → Entry under Guides
│ └── configuration.md # → Entry under Guides
└── api/
├── index.md # → "API" category
└── reference.md # → Entry under API
Use sidebar.position in frontmatter:
---
title: Installation
sidebar:
position: 1 # Appears first in its category
---
Pages without sidebar.position appear at the end, sorted alphabetically by title.
To exclude a page from the sidebar while keeping it accessible via URL:
---
title: Hidden Page
sidebar:
render: false
---
Pagination is automatic based on sidebar order.
Disable pagination:
---
pagination_next: null
pagination_prev: null
---
Link to a specific page:
---
pagination_next: advanced/deployment.md
---
export default defineConfig({
integrations: [
shipyard({ /* ... */ }),
shipyardDocs({ routeBasePath: 'docs', collectionName: 'docs' }),
shipyardDocs({ routeBasePath: 'api', collectionName: 'api-docs' }),
],
})
With content collections:
const docs = defineCollection(createDocsCollection('./docs'))
const apiDocs = defineCollection(createDocsCollection('./api-docs'))
export const collections = { docs, 'api-docs': apiDocs }
Organize docs by locale:
docs/
├── en/
│ └── getting-started.md
├── de/
│ └── getting-started.md
Locale-based routing is automatic when Astro’s i18n is configured.
When using Astro’s i18n with prefixDefaultLocale: true, documentation pages require the locale prefix (e.g., /en/docs/ instead of /docs/). To improve user experience, add redirects in your astro.config.mjs:
export default defineConfig({
redirects: {
'/': { status: 302, destination: '/en' },
'/docs': { status: 302, destination: '/en/docs' },
'/blog': { status: 302, destination: '/en/blog' },
},
i18n: {
defaultLocale: 'en',
locales: ['en', 'de'],
routing: { prefixDefaultLocale: true },
},
// ...
})
Note: Astro’s static redirects only work for exact paths. For wildcard redirects (e.g., /docs/* → /en/docs/*), configure your hosting provider’s redirect rules or use SSR mode with middleware.
shipyard-docs can automatically generate llms.txt and llms-full.txt files following the llms.txt specification. These files help Large Language Models efficiently parse and understand your documentation.
Enable llms.txt generation by adding the llmsTxt option:
shipyardDocs({
llmsTxt: {
enabled: true,
projectName: 'My Project',
summary: 'A brief description of your project for LLMs',
description: 'Optional additional context about your project.',
sectionTitle: 'Documentation', // Optional, defaults to 'Documentation'
},
})
When enabled, several files are automatically generated at build time under the docs path:
| File | Description |
|---|---|
/{routeBasePath}/llms.txt | Index file with links to plain text versions of each documentation page |
/{routeBasePath}/llms-full.txt | Complete file with full content of all documentation pages |
/{routeBasePath}/_llms-txt/*.txt | Individual plain text/markdown files for each documentation page |
For example, with the default routeBasePath of docs:
/docs/llms.txt - Main index file linking to individual plain text pages/docs/llms-full.txt - All documentation content in one file/docs/_llms-txt/getting-started.txt - Plain text version of a single pageThe llms.txt file links directly to .txt files (not HTML pages), following the approach used by Astro’s documentation.
When llms.txt is enabled, a link to /docs/llms.txt is automatically added to the sidebar. This link includes a copy-to-clipboard button, making it easy to copy the URL and paste it into AI assistant prompts.
When Astro’s i18n is configured, llms.txt only includes content from the default locale. This prevents mixing different languages in the same file, ensuring LLMs receive consistent, single-language documentation.
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable llms.txt generation |
projectName | string | 'Documentation' | H1 heading in the generated file |
summary | string | — | Brief project summary (displayed as blockquote) |
description | string | — | Additional context paragraphs |
sectionTitle | string | 'Documentation' | Title for the links section |
The generated llms.txt follows this format:
# My Project
> A brief description of your project for LLMs
Optional additional context about your project.
## Documentation
- [Getting Started](https://example.com/docs/_llms-txt/getting-started.txt): Installation and setup guide
- [Configuration](https://example.com/docs/_llms-txt/configuration.txt): Configuration options reference
Each linked .txt file contains the raw markdown content of that documentation page, making it easy for LLMs to parse the content directly without HTML overhead.
This makes your documentation easily accessible to AI coding assistants like Claude, Cursor, and others that support the llms.txt standard.
shipyard-docs supports multi-version documentation, allowing you to maintain separate content for different versions of your project.
1. Configure version options:
shipyardDocs({
routeBasePath: 'docs',
versions: {
current: 'v2',
available: [
{ version: 'v2', label: 'Version 2.0 (Latest)' },
{ version: 'v1', label: 'Version 1.0', banner: 'unmaintained' },
],
deprecated: ['v1'],
stable: 'v2',
},
})
2. Set up versioned content collection:
// content.config.ts
import { defineCollection } from 'astro:content'
import { createVersionedDocsCollection } from '@levino/shipyard-docs'
const docs = defineCollection(
createVersionedDocsCollection('./docs', {
versions: ['v1', 'v2'],
})
)
export const collections = { docs }
3. Organize content by version:
docs/
├── v2/
│ ├── getting-started.md
│ ├── configuration.md
│ └── new-feature.md
└── v1/
├── getting-started.md
└── configuration.md
| Option | Type | Required | Description |
|---|---|---|---|
current | string | Yes | Default version shown to users |
available | SingleVersionConfig[] | Yes | List of all available versions |
deprecated | string[] | No | Versions to show deprecation warnings |
stable | string | No | Stable version (defaults to current) |
| Option | Type | Required | Description |
|---|---|---|---|
version | string | Yes | Version identifier (e.g., 'v2', '2.0') |
label | string | No | Display label in UI (defaults to version) |
path | string | No | URL path segment (defaults to version) |
banner | 'unreleased' | 'unmaintained' | No | Banner type to display |
With versioning enabled, routes follow this pattern:
/docs/v2/getting-started/en/docs/v2/getting-startedSpecial routes:
/docs/ redirects to /docs/[current-version]//docs/latest/ is an alias for the current versionWhen versioning is enabled with multiple versions, a version selector dropdown automatically appears in the navbar and sidebar. The selector shows:
banner: 'unreleased'Content organization:
Version naming:
v1, not v1.0)label option for display-friendly namespath option if you need custom URL segmentsDeprecation workflow:
banner: 'unmaintained' to the version configdeprecated arrayFor a complete guide, see the Versioning Guide.