versioned documentation with jekyll collections
Why Documentation Versioning Matters
As software evolves, so does its documentation. Managing different versions of your docs is essential, especially when older versions remain in use. A versioning system allows users to view instructions that apply specifically to the version they’re using, reducing confusion and improving support quality. Fortunately, Jekyll provides tools like collections and Liquid templating that make versioned documentation easy to manage—even in static environments like GitHub Pages.
Common Approaches to Versioning
Before jumping into implementation, consider the main ways documentation is versioned:
- Directory-based versioning:
/v1/,/v2/ - Subdomain-based versioning:
v1.example.com - Query parameter-based versioning:
?version=2.0(not recommended for static sites)
We’ll focus on the first approach—directory-based versioning—since it fits seamlessly into Jekyll and GitHub Pages.
Setting Up Collections Per Version
Step 1: Define Collections in _config.yml
collections:
v1:
output: true
permalink: /v1/:path/
v2:
output: true
permalink: /v2/:path/
This declares two collections: v1 and v2. Each will have its own directory and output HTML pages with URLs prefixed by the version name.
Step 2: Create Collection Folders
Create the following folders in your root directory:
_v1/
_v2/
Each collection will hold version-specific content. You might duplicate files from one version to another and make only necessary changes, or maintain a completely separate structure depending on the level of difference.
Step 3: Add Versioned Pages
Create Markdown files inside each versioned folder, like:
_v1/getting-started.md
---
title: Getting Started (v1)
version: v1
---
Welcome to version 1 of our documentation.
_v2/getting-started.md
---
title: Getting Started (v2)
version: v2
---
Welcome to version 2 with new features.
Each file can now be rendered at /v1/getting-started/ and /v2/getting-started/ respectively.
Creating a Version Switcher
Step 1: Store Page Metadata
To enable cross-version switching, make sure each page includes a common identifier like slug:
slug: getting-started
Step 2: Generate Links Across Versions
In your layout, use Liquid to dynamically build links to other versions:
{% assign current_slug = page.slug %}
{% assign versions = site.collections | map: "label" %}
<ul class="version-switcher">
{% for version in versions %}
{% assign docs = site[version] | where: "slug", current_slug %}
{% if docs.size > 0 %}
<li><a href="{{ docs[0].url }}">{{ version }}</a></li>
{% endif %}
{% endfor %}
</ul>
This code loops through all collections, finds the same slug across versions, and generates links. This is especially useful for header dropdowns or sidebars.
Default Version Routing
To make the newest version your default, create a redirect:
Step 1: Add an Index Page
In your root, add an index.md with this front matter:
---
layout: redirect
redirect_to: /v2/
---
Step 2: Create a Redirect Layout
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0; url={{ page.redirect_to }}" />
</head>
</html>
This makes your root path redirect to the latest version.
Dynamic Side Navigation per Version
Because each version is a separate collection, you can generate sidebars dynamically. Example:
{% assign docs = site[page.version] %}
<ul class="sidebar">
{% for doc in docs %}
<li><a href="{{ doc.url }}">{{ doc.title }}</a></li>
{% endfor %}
</ul>
This ensures each version of your documentation shows only relevant links.
Maintaining and Updating Versions
Minimize Duplication
If only a few files differ between versions, consider using shared includes or layouts. You can define common chunks in _includes/ and use conditional logic:
{% if page.version == 'v1' %}
{% include old-api-warning.html %}
{% endif %}
Automate Cloning with Scripts
When releasing a new version, you can duplicate an entire collection folder using a build script:
cp -R _v2 _v3
Then update _config.yml to add v3 as a new collection.
Search Compatibility Across Versions
If you use Lunr.js or similar, you can index all versions or one version at a time. Add a version field to each indexed document and let users filter results by version.
Real-World Example
Many open-source projects use this pattern. Jekyll’s own documentation uses version folders to host multiple docs side-by-side. Popular frameworks like Vue, Angular, and React also use directory-based versioning to manage major releases.
Summary and Next Steps
By using Jekyll collections and Liquid templates, you can build a scalable, maintainable system for versioned documentation—completely static and GitHub Pages friendly. It’s an ideal solution for open-source projects, SaaS products, and APIs with multiple iterations.
In the next article, we’ll explore how to automate navigation menus from data files to reduce manual updates and support nested documentation structures across versions.
