Versioning and Prerelease
|
This content was generated with the assistance of AI and should be verified against the official documentation of each tool before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
This page was written against Antora 3.2.1 and checked on 2026-10-26. Every example was built with that version except where the text says otherwise. A
component version in Antora is the pair of a component name and a version, and almost everything about a versioned site
(the version selector, the URLs, the target of an xref that names no version) follows from how that version is
defined, sorted and classified as stable or prerelease.
What a version is and why it matters
Antora does not understand releases; it only understands git references (branches and tags) that contain a component
version descriptor, antora.yml. The version key of that descriptor, together with name, identifies one component
version. Antora uses the version to:
-
sort the component versions of a component,
-
find the latest version of a component,
-
build the version segment of published URLs (
/acme/2.0/index.html), -
label the entry in the component version selector and the page version selector of the UI,
-
resolve the version coordinate in resource IDs (
2.0@acme::index.adoc).
Several pieces of the same idea are kept apart on purpose. The actual version is the value of version. The
prerelease marker, the display version and the symbolic version each override one default behaviour of the actual
version and nothing else, as the following table (derived from the Antora version-facets page) shows.
| Facet | In resource IDs | Sorting and routing | Shown in UI menus | In URLs |
|---|---|---|---|---|
Actual version ( |
Yes |
Yes |
Yes, unless replaced |
Yes, unless replaced |
Prerelease ( |
No |
Yes |
Yes, appended to the version |
No |
Display version ( |
No |
No |
Yes |
No |
Symbolic version ( |
No |
No |
No |
Only for the latest version |
Symbolic prerelease version ( |
No |
No |
No |
Only for the latest prerelease |
Where the version comes from
The version key can be written in the descriptor stored in each git reference or, as a fallback, on the content
source in the playbook. When both exist, the descriptor wins. If you want the playbook to control the version, leave the
key out of antora.yml. The accepted forms are the same in both places.
| Form | Example | Meaning |
|---|---|---|
Named identifier |
|
Any value that is not semantic. Sorted before semantic versions. |
Semantic identifier |
|
An integer, or a string that starts with an integer and contains a dot. A leading |
Versionless |
|
The tilde (or |
Refname |
|
The short name of the branch or tag becomes the version. Forward slashes are replaced with hyphens. |
Refname projection |
A map of patterns to replacements |
The version is derived from the refname. Antora uses the first pattern that matches and falls back to the refname if none does. |
A literal value may contain letters, digits, periods, underscores and hyphens, and must not contain spaces, forward
slashes or the characters &, < and >. Use lowercase letters for portability between web servers. A fixed value on
a playbook content source is only sensible when the source matches a single reference; otherwise use true or a
projection.
A projection is a map from a glob (with the same syntax as the branches and tags patterns) to a replacement. Groups
in parentheses can be named with ?<name> and referenced as $<name>, or referenced by position ($1), and $& is the
whole refname. The next playbook takes versions from tags named v1.0.0, v1.1.0 and v2.0.0 and from a release
branch, none of which has a version key in its antora.yml.
site:
title: Widgets Docs
url: https://docs.example.com
content:
sources:
- url: ./tagged
branches: release/*
tags: v*
version:
'v(?<major>*).(?<minor>*).*': $<major>.$<minor>
'release/(*)-rc': $1
Source: Antora docs: Fallback Version
The branch release/3.0-rc has prerelease: -rc.1 in its descriptor. The result, printed by the small extension shown
later on this page, is:
component widgets: latest=2.0 latestPrerelease=3.0
3.0 displayVersion="3.0-rc.1" prerelease="-rc.1" url=/widgets/3.0/index.html
2.0 displayVersion="2.0" prerelease=undefined url=/widgets/2.0/index.html
1.1 displayVersion="1.1" prerelease=undefined url=/widgets/1.1/index.html
1.0 displayVersion="1.0" prerelease=undefined url=/widgets/1.0/index.html
Replacing the projection with version: true on the same sources produced the versions v2.0.0, v1.1.0, v1.0.0
and release-3.0-rc (the slash became a hyphen), and the URL /widgets/release-3.0-rc/index.html. Branch versioning
(one long-lived branch per release, such as v1.0.x) and tag versioning (one tag per release) both work; what matters
is only that each reference carries a descriptor, and that the name and version pair is unique.
== How versions are sorted
Antora only looks at the version key when it sorts, never at display_version or the URL. It recognises three
schemes.
| Scheme | Matches | Examples |
|---|---|---|
Semantic |
A string that starts with a number (optionally preceded by |
|
Named |
Everything that is neither semantic nor the reserved empty value |
|
Versionless |
|
(empty) |
The ordering rules are:
-
A versionless component version sorts first, before named and semantic versions.
-
Named versions come before semantic versions and are sorted in reverse alphabetical order. Uppercase letters sort before lowercase ones and are not recommended.
-
Semantic versions come last, in descending order, using semantic-versioning rules and ignoring a leading
v.
For example, the versions v2.5, 4.0, 3.9, utopia and vivid appear in the order vivid, utopia, 4.0, 3.9,
v2.5. Components themselves are sorted alphabetically by title in the UI model (site.components), falling back to
the name when there is no title.
The latest version of a component is the first version in that sorted list which is not a prerelease. If every version
is a prerelease, the first prerelease is the latest. A versionless component version is always the latest version, or
the latest prerelease if it sets prerelease. The latest version is exposed to the UI as the latest property of the
component.
A named version therefore beats every number. In this build, a branch with version: edge next to the semantic
versions 1.0, 2.0, 2.5 and a prerelease 3.0 takes the lead:
component acme: latest=edge latestPrerelease=none
edge displayVersion="edge" prerelease=undefined url=/acme/edge/index.html
3.0 displayVersion="3.0-beta.2" prerelease="-beta.2" url=/acme/3.0/index.html
2.5 displayVersion="2.5" prerelease=undefined url=/acme/2.5/index.html
2.0 displayVersion="2.0" prerelease=undefined url=/acme/2.0/index.html
1.0 displayVersion="1.0" prerelease=undefined url=/acme/1.0/index.html
Two details of this output are worth knowing. edge is the latest version, so inbound references that name no version
land on it. And latestPrerelease is none because 3.0 sorts after edge: the latest prerelease only exists when a
prerelease sorts ahead of the latest stable version.
Prerelease
The prerelease key marks a component version as not yet stable without changing its identity. It accepts an
identifier (a non-empty string such as -rc.3, .alpha.2, -M1, SNAPSHOT or Beta) or true. Set it to false, or
remove it, when the version is released; the version, its URLs and the references that point to it do not change.
name: acme
title: Acme
version: '3.0'
prerelease: -beta.2
nav:
- modules/ROOT/nav.adoc
What Antora does with a prerelease:
-
It computes a prerelease version. If the identifier starts with a hyphen or a dot it is appended directly (
3.0and-beta.2give3.0-beta.2); any other identifier is appended after a space (4.0andBetagive4.0 Beta). This value is used for sorting and, unlessdisplay_versionis set, is shown in the UI menus. Withprerelease: truethe version is shown unchanged. -
It skips the version when picking the latest version, unless every version of the component is a prerelease.
-
It does not route a reference to it when the reference gives no version. Such references go to the latest stable version.
In the previous listings 3.0 appears with the display version 3.0-beta.2 and the URL /acme/3.0/index.html, so the
default URL does not tell readers that it is a prerelease. The prerelease key never changes a URL by itself; only
latest_prerelease_version_segment, described below, does.
Display version
display_version changes only what the UI menus show. It may contain spaces, uppercase letters and symbols, such as
'5.6 EOL' or 3.0 Beta. It is never used in resource IDs, sorting or URLs, and when both display_version and
prerelease are set the display version is shown but the computed prerelease version still decides the order.
Versionless component versions are labelled default in the menus unless a display version or a prerelease identifier
says otherwise.
Latest version segments
By default the version segment of a URL is the actual version, so the latest version is not recognisable from the
address. Two playbook keys under urls replace the segment for the latest version and the latest prerelease of every
component. Both keys apply to the whole site and cannot be set in antora.yml.
| Key | Effect |
|---|---|
|
The symbolic version used in place of the actual version in URLs of the latest version of each component, for example
|
|
The same for the latest prerelease, for example |
|
Chooses between replacing the URL and redirecting (see below). |
The values follow the same character rules as version. The value cannot be null, and it can only be an empty string
with the replace and redirect:to strategies. With an empty string, the latest version is published with no version
segment at all. It is not applied to versionless component versions.
Replace (the default)
If the playbook sets a symbolic version and no strategy, Antora uses replace at run time: the latest version is
published only under the symbolic URL. The playbook below was built with the versions 1.0, 2.0, 2.5 and the
prerelease 3.0 from the earlier example, without the edge branch.
site:
title: Acme Docs
url: https://docs.example.com
content:
sources:
- url: ./acme
branches: [v1.0.x, v2.0.x, v2.5.x, v3.0.x]
urls:
latest_version_segment: current
latest_prerelease_version_segment: next
component acme: latest=2.5 latestPrerelease=3.0
3.0 displayVersion="3.0-beta.2" prerelease="-beta.2" url=/acme/next/index.html
2.5 displayVersion="2.5" prerelease=undefined url=/acme/current/index.html
2.0 displayVersion="2.0" prerelease=undefined url=/acme/2.0/index.html
1.0 displayVersion="1.0" prerelease=undefined url=/acme/1.0/index.html
The generated site contains acme/1.0, acme/2.0, acme/current and acme/next; there is no acme/2.5 directory.
When 2.6 is released and becomes the latest version, 2.5 moves to acme/2.5 and current points to 2.6. Because
current is not a version, bookmarks to it stay valid but the content behind it changes. Setting
latest_version_segment: '' instead published the latest version at /acme/index.html, and the other versions under
their numbers.
Redirect strategies
redirect:to and redirect:from keep both addresses alive by generating temporary (302) redirects. They need a
redirect facility other than the default static, because only a web server can answer with a redirect.
| Strategy | Behaviour |
|---|---|
|
Default. The actual version is replaced by the symbolic version; no redirect rules are generated. |
|
The page is published under the symbolic URL, and the URL with the actual version redirects to it. Without a
redirect facility it becomes |
|
The page stays under the actual version, and the symbolic URL redirects to it. The symbolic version cannot be empty
with this strategy. It is ignored when the facility is |
The next playbook fragment used redirect:to with the Netlify facility, and Antora wrote a _redirects file at the
site root:
urls:
latest_version_segment: current
latest_version_segment_strategy: redirect:to
redirect_facility: netlify
/acme/2.5/* /acme/current/:splat 302!
In that build the prerelease 3.0 remained at /acme/3.0/ because no latest_prerelease_version_segment was set.
Branches to versions to URLs
The diagram summarises what the builds above did. Each branch or tag contains a descriptor; Antora resolves a version, sorts the versions, marks the latest and the latest prerelease, and finally decides the version segment for the URL.
The 3.2 change: versionSegment
The Antora 3.2 release notes (issue 1006) say that the content classifier now uses the versionSegment property of a
component version, when present, instead of the version in the output path and URL of pages, and that it sets a dynamic
activeVersionSegment property telling which segment is in use. These are properties of the component version in the
content catalog that extensions work with; the official documentation does not list versionSegment among the keys of
antora.yml. A site that uses no extension setting it is unaffected, and latest_version_segment and its strategies
work as described above. An extension that builds URLs by hand should read activeVersionSegment rather than version,
because the latest version is published under the symbolic segment.
The version also appears in resource IDs. A reference that gives no version resolves to the latest version of the
target component, but never to a prerelease unless every version is a prerelease. A reference can pin a version with the
coordinate before the component, and a versionless target can be addressed with _. The page that follows
gives the full grammar; this build is only the effect on versions. A page with the two references
* xref:widgets::index.adoc[Latest stable (no version)]
* xref:3.0@widgets::index.adoc[Pinned to the prerelease]
Source: Antora docs: Create Page Links
produced links to ../widgets/2.0/index.html for the first and ../widgets/3.0/index.html for the second, with 3.0
being a prerelease. See Resource IDs and Xrefs for the
complete syntax.
Printing the versions Antora sees
The listings in this page come from a few lines of extension code. It registers for the contentClassified event, when
the content catalog already holds the sorted component versions.
module.exports.register = function () {
this.once('contentClassified', ({ contentCatalog }) => {
for (const component of contentCatalog.getComponents()) {
console.log(`component ${component.name}: latest=${component.latest.version || '(empty)'} ` +
`latestPrerelease=${component.latestPrerelease ? component.latestPrerelease.version : 'none'}`)
for (const cv of component.versions) {
console.log(` ${(cv.version || '(empty)').padEnd(6)} displayVersion=${JSON.stringify(cv.displayVersion)} ` +
`prerelease=${JSON.stringify(cv.prerelease)} url=${cv.url}`)
}
}
})
}
Enable it with antora.extensions in the playbook or --extension ./print-versions.js on the command line. See
Antora Extensions for the extension API.
Pocket Units
Pocket Units keeps one branch per release in its documentation repository. The branches v1.0 and v2.0 each carry an
antora.yml with name: pocket-units and their own version, and a second repository holds the versionless component
shared, which declares version: ~. Building these with the default settings produced
pocket-units/1.0/index.html
pocket-units/2.0/index.html
shared/style-guide.html
The shared component has no version segment, as the rules require. With
latest_version_segment: current, the pages of 2.0 would move to pocket-units/current/. To publish a 3.0 preview
without it becoming the default target of references, add a v3.0 branch with prerelease: -beta.1, and
optionally publish it at next with latest_prerelease_version_segment. This 3.0 preview was not built for Pocket Units;
the acme builds above show the same behaviour.
Pitfalls and notes
-
A numeric-looking version must be quoted in YAML (
version: '2.0'); an unquoted2.0is a number and1.10would become1.1. -
A named version such as
edge,mainornextsorts before every number and becomes the latest version. Keep named branches for a component that has only named versions, or give the development branch a semantic version withprerelease. -
The version is not allowed to contain
/. A projection orversion: trueconverts slashes to hyphens, sorelease/3.0becomesrelease-3.0. -
prereleasedoes not change URLs. Uselatest_prerelease_version_segmentif you want an address that reveals it. -
The
redirect:toandredirect:fromstrategies are silently inactive with the defaultstaticfacility. Setredirect_facilitytonetlifyornginxand deploy to a host that honours the generated file. -
Changing
latest_version_segmentmoves published URLs. Treat the segment as a stable public name. -
The latest version is computed per component, so each component of a site has its own
currentversion.