Several versions
Serve the same documentation at several refs, with a switcher and a banner.
A ref becomes a version. Point a source at tags and each one is published, with a version segment in the URL and an entry in the switcher.
Steps
- List the refs. A bare string is a branch. A tag has to say so — git
keeps the two in separate namespaces, and asking for a tag under
refs/headsfails the build.sources: [ { repo: 'acme/api', path: 'docs', refs: [ { branch: 'main', status: 'upcoming' }, { tag: 'v2.0.0' }, { tag: 'v1.4.0' }, { tag: 'v0.9.0', status: 'eol' } ] } ], sourceOptions: { defaultRef: 'v2.0.0' } - Name the default.
defaultRefis the ref served without a version prefix and the one search engines are pointed at. Left unset it is the first in the list — which is rarely what you want when the list starts at a branch. - State the lifecycle where it cannot be derived. Semver orders tags, so
"older than the default" needs no help. It cannot place a branch against a
tag:
mainisupcoming, and without saying so its reader is told to upgrade to a version older than what they are reading. - Use
latestif you do not want to edit this list every release. It resolves at build time to the newest semver tag of that repository.refs: [{ branch: 'main', status: 'upcoming' }, 'latest'] - Mark a page's version requirement in the prose, where a reader looking at
one version cannot see the others:
### Retries :since{version="v2.0.0"}
How coarse a version is
The name is the site's choice, not the mechanism's, and it is the decision that matters most here — it is what every link to your documentation will carry.
A line per version, as a branch. What Laravel, Symfony and Django do:
11.x, 10.x, 9.x are branches that keep moving, and a patch release changes
nothing about the URL.
sources: [
{
path: 'docs',
refs: [
{ branch: '12.x', status: 'upcoming' },
'11.x',
{ branch: '10.x', status: 'deprecated' },
{ branch: '9.x', status: 'eol' }
]
}
],
sourceOptions: { defaultRef: '11.x' }
A bare string is a branch; only a tag has to say so.
A line per version, from tags. Where the release you want to publish is a
tag but the URL should not move with every patch, label renames it:
refs: [
{ tag: 'v2.4.1', label: 'v2' },
{ tag: 'v1.9.3', label: 'v1', status: 'deprecated' }
]
Every release its own version. { tag: 'v2.4.1' } with no label. Each is
citable forever, and the switcher grows by one entry per release — which is the
right answer for a specification and the wrong one for a manual.
The same choice applies to a section that names its own versions: version: 'v2'
or version: '2.4.1', with exactly the same trade-off. One model, one decision,
whether the content comes from a ref or from a file.
A version segment is part of every link anyone ever makes to your documentation.
v2 survives the next forty releases; 2.4.1 is dead the day 2.4.2 ships.
When the versions are not refs
A version is normally a checkout. Two shapes are not, and both are ordinary:
Documentation in folders. docs/v1 beside docs/v2 in one repository, no
tags. Each source names the version it is:
sources: [
{ path: 'docs/v2', version: 'v2' },
{ path: 'docs/v1', version: 'v1', status: 'deprecated' }
]
An API in files. openapi/v1.yaml beside openapi/v2.yaml — the usual way
an API is kept, and the versions belong to the section, not to the source:
generated: [
{
type: 'openapi',
label: 'API',
path: 'openapi/v2.yaml',
versions: [
{ version: 'v2', path: 'openapi/v2.yaml' },
{ version: 'v1', path: 'openapi/v1.yaml', status: 'deprecated' }
]
}
]
Both produce exactly what refs produce: a segment in the URL, an entry in the
switcher, the banner and the canonical. The switcher stays scoped to the artefact
you are reading — on /api it offers the API's versions, on a prose page the
documentation's.
A source that lists refs and names a version, or a section with
versions on a source that has refs, fails the build: both want the same segment
of the URL. See Sources for the full field list.
What happens to the old versions
A non-default version carries noindex and a canonical pointing at the same
page in the default one, and an eol version leaves the sitemap entirely. That
is what stops a search engine offering v0.9.0 where the reader wanted today's
documentation — see URLs and versions.
Checklist
- Every tag is written as
{ tag: … }, not as a bare string - The version names are as coarse as the project's own support lines
-
defaultRefnames the version readers should land on - A branch in the list carries an explicit
status - The switcher shows every version, with a badge on the dead ones
- A dead version's page is absent from
/sitemap.xml - Where the versions are folders or files rather than refs, exactly one of
refs, a source'sversionand a section'sversionsis in play