Document version management

SveltePress can keep the latest documentation at its normal URLs while publishing immutable historical snapshots below a version prefix such as /v/8.1/. Version management is opt-in: sites without a sveltepress.versions.json manifest behave exactly as before.

Install and initialize

Install the CLI beside the Vite plugin and theme:

pnpm add -D @sveltepress/cli
sh

Initialize version management with the version currently served by your site:

pnpm exec sveltepress versions init --current 8.1 --label "8.1"
sh

This creates sveltepress.versions.json. The Vite plugin discovers that file automatically; use sveltepress({ versions: false }) to disable discovery or sveltepress({ versions: { manifest: 'path/to/versions.json' } }) to select another manifest.

Enable incremental artifacts

Existing sites can migrate their committed snapshot directories once. New sites can run the same command immediately after versions init:

pnpm exec sveltepress versions migrate --site-id docs-example
sh

Migration replaces full src/routes/v/{id} copies with committed version-deltas/{id} source deltas and initializes a content-addressed page store under .sveltepress/version-artifacts. The store is a build cache; commit the source deltas and restore/cache the artifact store in CI.

Use the incremental command as the production build script:

{
  "scripts": {
    "build": "sveltepress versions build"
  }
}
json

versions plan reports compiled, reused, removed, and recomposed routes without building. versions build restores missing historical artifacts from committed deltas, compiles only changed current pages, composes the stable SveltePress shell, and then runs the normal Vite production build. A shell or index change may recompose routes without recompiling unchanged page content; a page compiler or artifact schema change intentionally invalidates every page artifact.

For GitHub Actions, restore the newest compatible store before the build and save the updated store under the current commit key:

- uses: actions/cache@v4
  with:
    path: .sveltepress/version-artifacts
    key: sveltepress-pages-${{ runner.os }}-${{ github.sha }}
    restore-keys: |
      sveltepress-pages-${{ runner.os }}-
- run: pnpm build
yaml

Use the equivalent persistent-cache facility on other CI providers. Do not share a store between different siteId values.

Generated page modules, including Default Theme LiveCode components, are embedded in their owning page artifacts. Cache only .sveltepress/version-artifacts; .sveltepress/live-code is a local development directory and is not required when a CI worker restores reusable pages.

LiveCode artifact self-check

This page dogfoods that behavior. The interactive component below is generated from this Markdown file, embedded in the page artifact, and server-rendered again after the local .sveltepress/live-code directory is removed. If the card renders and its button responds, both the reusable artifact and client hydration paths are working.

LIVE DOCUMENTATION CHECK

Artifact self-check passed

  • Generated module embedded
  • Server render complete
  • Client hydration ready
<script>
  let  = (0)
  const  = [
    'Generated module embedded',
    'Server render complete',
    'Client hydration ready',
  ]
</script>

<section ="artifact-check" data-version-artifact-live-code>
  <div ="artifact-check__status" aria-hidden="true"></div>
  <div ="artifact-check__content">
    <p ="artifact-check__eyebrow">LIVE DOCUMENTATION CHECK</p>
    <h3>Artifact self-check passed</h3>
    <ul>
      {#each  as }
        <li><span aria-hidden="true"></span>{}</li>
      {/each}
    </ul>
    <button ="button" ={() => ++}>
      Test interaction{ ? ` · ${}` : ''}
    </button>
  </div>
</section>

<style>
  .artifact-check {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 1rem;
    overflow: hidden;
    padding: 1.25rem;
    border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
    border-radius: 1rem;
    background:
      radial-gradient(circle at 100% 0%, rgb(255 94 122 / 18%), transparent 45%),
      color-mix(in srgb, currentColor 4%, transparent);
  }

  .artifact-check__status {
    display: grid;
    width: 2.75rem;
    height: 2.75rem;
    place-items: center;
    border-radius: 0.85rem;
    color: #14231a;
    font-size: 1.4rem;
    font-weight: 800;
    background: #70e19b;
    box-shadow: 0 0 0 0.35rem rgb(112 225 155 / 12%);
  }

  .artifact-check__content h3,
  .artifact-check__content p {
    margin: 0;
  }

  .artifact-check__eyebrow {
    color: #ff5e7a;
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 0.14em;
  }

  .artifact-check__content h3 {
    margin-top: 0.15rem;
    font-size: 1.2rem;
  }

  .artifact-check__content ul {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.85rem 0;
    padding: 0;
    list-style: none;
  }

  .artifact-check__content li {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.3rem 0.55rem;
    border-radius: 999px;
    font-size: 0.78rem;
    background: color-mix(in srgb, currentColor 8%, transparent);
  }

  .artifact-check__content li span {
    color: #45c97c;
    font-weight: 800;
  }

  .artifact-check__content button {
    padding: 0.55rem 0.8rem;
    border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
    border-radius: 0.65rem;
    color: inherit;
    font: inherit;
    font-size: 0.85rem;
    font-weight: 700;
    background: transparent;
    cursor: pointer;
  }

  .artifact-check__content button:hover {
    border-color: #ff5e7a;
  }
</style>
svelte
Expand code
View code

Create a release snapshot

Advance before editing the next version

Start from clean, complete outgoing documentation. Build and create the next version before editing its pages or adding its :::since markers. create freezes the outgoing current version and makes the supplied ID current; only then should new documentation use that ID.

pnpm exec sveltepress versions build
pnpm exec sveltepress versions create 8.2 --label "8.2"

# 8.2 is now current: edit docs and add version="8.2" markers

pnpm exec sveltepress versions build
pnpm exec sveltepress versions validate
sh

Never run versions create over documentation that already contains the next version's edits. Those edits would be frozen into the outgoing version. If work started early, restore the known clean outgoing state, build and advance it, then reapply the edits to the new current version.

CLI --locale for per-locale manifests

Multi-locale sites keep one versions manifest per locale (sveltepress.versions.json, sveltepress.versions.zh.json, sveltepress.versions.bn.json, …). Pass --locale <id> to init, create, validate, and other versions subcommands so they read and write that locale's manifest and deltas (for example version-deltas-zh/). Default locale commands omit --locale.

sveltepress versions build with no --locale drafts every locale, then composes /v/, /zh/v/, /bn/v/, … into one production output. Use --locale zh (optionally with --draft-only) only when you need a single-locale job.

sveltepress versions build --locale zh
sveltepress versions create 8.2 --label "8.2" --locale zh
sveltepress versions validate --locale zh
sh

See Internationalization for how locales and version bases compose (/zh/v, /bn/v).

Each locale freeze is independent: run build then create per locale (default, then --locale zh, --locale bn, …). A default-locale create must only capture unprefixed English routes — never src/routes/zh/ or src/routes/bn/. Locale creates write logical routes into version-deltas-<locale>/ (no double /zh/v/.../zh/... prefixes). If a freeze accidentally included sibling locale trees, recreate it with the locale-scoped CLI rather than hand-editing deltas.

create publishes the current draft manifest, writes only changed source pages and tombstones to version-deltas/8.1/, freezes route/sidebar/change metadata, moves 8.1 into history, and makes 8.2 current. It refuses stale drafts, duplicate IDs, symbolic links, a dirty Git worktree, and dependencies outside the frozen boundary. Use --allow-dirty only when the uncommitted state is intentionally the release source.

Published versions also receive a generated sourceHash. Each delta binds the frozen route, sidebar, and change catalog with a metadata hash. versions validate reconstructs every committed delta and checks both hashes, so source or metadata drift is detected even when the artifact cache is empty. Do not edit the hashes or delta files by hand.

The operation is atomic: a failed preflight does not leave a partial delta or update the manifest. versions list prints the version order, versions publish 8.1 prints the immutable manifest hash for CI publication, and versions gc --dry-run reports unreferenced local blobs before cleanup.

Manifest

{
  "$schema": "./node_modules/@sveltepress/cli/schema/versions.schema.json",
  "basePath": "/v",
  "current": { "id": "8.2", "label": "8.2" },
  "versions": [
    {
      "id": "8.1",
      "label": "8.1",
      "status": "deprecated",
      "message": "Upgrade to 8.2 for fixes.",
      "sourceRef": "v8.1.0",
      "search": { "facetFilters": ["version:8.1"] }
    }
  ],
  "content": {
    "include": ["**"],
    "exclude": ["internal/**"],
    "shared": ["$lib/**", "static/**"]
  },
  "artifacts": {
    "mode": "incremental",
    "siteId": "docs-example",
    "store": ".sveltepress/version-artifacts",
    "sources": "version-deltas"
  }
}
json

Version IDs are URL-safe lowercase identifiers and may contain dots or hyphens. include and exclude select route files to freeze. shared explicitly allows dependencies that remain live instead of being copied; keep this list narrow because a later change can affect every historical version.

Set status to deprecated or eol to show a prominent site-wide banner above the navigation. It warns that older-site functionality may be unavailable and links to the current version of the same logical page. sourceRef redirects historical edit links to a matching Git ref; set editLink: false to hide them. EOL versions default to noindex, and noIndex: true can opt another version out of indexing.

Navigation and missing pages

The Default Theme adds an accessible version selector automatically. Internal links and the frozen sidebar stay in the selected historical version when the target route existed there. Switching versions preserves the logical page when possible; otherwise it opens that version's home page and displays an explanatory notice.

Custom themes can import virtual:sveltepress/versions. It exposes the validated manifest plus resolveVersionContext, resolveVersionedPath, and resolveVersionSwitch helpers.

Browser code that imports these helpers directly from the package should use @sveltepress/vite/versioning/runtime; this entry contains no Node file-system code. Build and configuration code can continue to use @sveltepress/vite/versioning.

Search, PWA, and generated files

Version-aware builds also:

  • emit canonical links for every page and noindex for configured versions;
  • include current and historical URLs in sitemap.xml;
  • keep root llms.txt/llms-full.txt current-only and write historical files below /v/{id}/;
  • exclude historical HTML from PWA precaching and fetch it with a network-first strategy.

Treat version-deltas as immutable release source: review and commit it, but do not edit it by hand. The artifact store can be restored from those deltas on a cold CI worker, while a persistent CI cache avoids recompiling historical pages. Make corrections in current routes and create the next release delta.

Describe what changed

SveltePress compares the current route inventory with the most recent historical version in manifest order. A route that only exists in the current version is listed as a new page. Use page frontmatter to provide a focused summary or keep a page out of the change catalog:

---
title: What's new
versionChanges:
  exclude: true
  summary: Optional overview summary
---
yaml

For an existing Markdown page, mark only the important new section. The version, title, and page-unique stable ID are required:

### Hot reload

Existing documentation content.

:::since[Hot reload configuration]{version="8.2" id="hot-reload" summary="No restart required"}
New documentation content.
:::
md

Unknown versions, duplicate IDs, unknown fields, and invalid field types stop development and production builds. New pages appear only under New pages; their since sections are not repeated under Updated pages. The first managed version has no comparison baseline and does not report the entire site as new.

Automatic "New" badges on pages, sections, and navigation

The Default Theme automatically marks newly introduced features across multiple navigation and content surfaces. Importantly, these badges are only shown while browsing the specific documentation version that introduced them; they automatically hide or adjust when switching to older or newer versions:

  1. Page title badge (page level):

    • When a page is newly added in the active version compared to the preceding baseline version (listed under newPages), the Default Theme automatically displays an accent badge beside the main page title (h1.page-title), such as New in 8.2.
    • The badge template defaults to "New in {version}". Customize it via i18n.versionNewLabel in theme options (where {version} is replaced with the active version's label or ID).
    • When browsing subsequent versions, the page is no longer considered new, so the title badge is automatically omitted.
  2. Section and paragraph badge (section level):

    • In existing pages, sections wrapped in :::since[Title]{version="8.2" id="unique-id" summary="..."} directives are rendered as stylized container boxes with an automatic version badge in their section header.
    • The section badge only displays when the user browses the exact version specified in version="...". When browsing other versions, the badge is hidden. The text template also follows i18n.versionNewLabel.
  3. In-page navigation badge ("On this page" / TOC level):

    • The right-side "On this page" table of contents automatically resolves associations between headings and :::since markers:
      • Headings contained inside a :::since marker are associated directly;
      • When a :::since marker contains no headings of its own, the Default Theme automatically associates it with the nearest preceding heading within the same Markdown container;
      • Multiple markers can belong to one heading.
    • While browsing the version that introduced those changes, any associated heading anchor in the table of contents automatically renders a compact New badge (VersionNavigationBadge).
    • The compact badge text defaults to "New". Override it with i18n.versionNavigationNewLabel.
  4. Sidebar navigation badge (sidebar level):

    • The sidebar automatically adds the compact New badge to both new pages (newPages) and updated pages (updatedPages), also configured via i18n.versionNavigationNewLabel.

What's New overview page

In addition to contextual badges on pages, sections, and navigation, you can embed the full release change catalog anywhere on your site:

src/routes/whats-new/+page.svelte
<script>
  import  from '@sveltepress/theme-default/VersionChanges.svelte'
</script>

< />
svelte

See the official site's live catalog on the What's new page .

Route-scoped What's New catalogs

VersionChanges defaults to the documentation version resolved from the current page URL. A valid ?version={id} explicitly overrides that context. Each version's catalog is generated against its immediately preceding version in manifest order, while frozen historical catalogs stay independent of later current documentation. Current links stay unprefixed; historical links target /v/{id}/..., including exact section anchors.

Custom themes can read the same immutable data from virtual:sveltepress/versions:

import {
  ,
  ,
} from 'virtual:sveltepress/versions'

const  = ()
const  = ('8.1')
ts

versions create freezes the outgoing current change set in its immutable artifact manifest and source delta. Historical catalogs are always read from frozen metadata rather than reconstructed from later current documentation, and versions validate detects malformed markers, invalid references, duplicate anchors, corrupt artifacts, and delta drift.