A Quarto extension that enables dynamic content switching in HTML documents. Perfect for documentation that needs to show different content for multiple versions, languages, or platforms.
- Display different content blocks based on user selection
- Support for both block-level and inline content switching
- Dropdown selector with customizable position and label
- Persistent selection using localStorage
- URL parameter support for direct linking to specific versions
- Auto-detection of versions from content
- Dark mode support
- Works seamlessly with Quarto themes
Install the extension in your Quarto project:
quarto add posit-dev/content-switcherOr install a specific version:
quarto add posit-dev/content-switcher@v0.1.0This will install the extension under the _extensions subdirectory of your project. If you're using version control, you'll want to check in this directory.
Add the extension to your document's YAML frontmatter:
---
title: "My Document"
format: html
filters:
- content-switcher
extensions:
content-switcher:
default: "v2026.01"
versions:
- id: "v2026.01"
label: "2026.01.0"
- id: "v2.0"
label: "2.0"
- id: "v3.0"
label: "3.0"
selector:
position: "header"
label: "Version:"
---Use divs with the content-switcher class to create version-specific content blocks:
::: {.content-switcher version="v2026.01"}
## Version 2026.01.0 Example {.unlisted}
Use pandas to read CSV data:
```python
import pandas as pd
data = pd.read_csv('data.csv')
```
:::
::: {.content-switcher version="v2.0"}
## Version 2.0 Example {.unlisted}
Use readr to read CSV data:
```r
library(readr)
data <- read_csv('data.csv')
```
:::Important: Always add {.unlisted} to headings inside content-switcher blocks to exclude them from the table of contents. Without this, the TOC will show headings for all versions simultaneously, even though only one version is visible at a time, leading to confusing TOC behavior.
Tip: Use level 2 headings (##) for content blocks that should be individually searchable (see Limitations section for details about search indexing).
Switch content inline within paragraphs using spans:
You can use [pandas]{.content-switcher version="v2026.01"}[readr]{.content-switcher version="v2.0"}[CSV.jl]{.content-switcher version="v3.0"} to read your data files.Content switcher blocks can be combined with other Quarto classes:
::: {.content-switcher version="v2026.01" .callout-note}
This is a version-specific note with callout styling.
:::Configure the extension in your document's YAML frontmatter under extensions: content-switcher::
| Option | Type | Default | Description |
|---|---|---|---|
default |
string | "default" |
The version to display by default |
versions |
array | [] |
List of version configurations |
selector.position |
string | "header" |
Where to place the selector: "header"/"top" (after title and description), "after-first-heading" (after first content heading), or "before-content" |
selector.show |
boolean | true |
Whether to show the version selector dropdown |
selector.label |
string | "Version:" |
Label text for the selector |
Each version in the versions array can be configured as:
extensions:
content-switcher:
versions:
- id: "unique-id" # Required: unique identifier used in version attributes
label: "Display Name" # Optional: user-friendly name shown in dropdown (defaults to id)Or as a simple string:
extensions:
content-switcher:
versions:
- "v1.0"
- "v2.0"The selector can be customized with these options:
extensions:
content-switcher:
selector:
position: "header" # Where to place the selector
label: "Version:" # Label text displayed next to dropdown
show: true # Whether to show the selector (default: true)Link directly to a specific version by adding a URL parameter:
https://example.com/docs.html?version=v2026.01
The URL parameter takes precedence over localStorage.
User selections are automatically saved in localStorage and persist across page visits within the same browser.
If no versions are specified in the configuration, the extension will automatically detect versions from content blocks.
When content switches between versions, a custom content-switcher:changed event is dispatched on the window object. You can listen for this event to trigger custom behavior:
window.addEventListener('content-switcher:changed', function(event) {
console.log('Switched to version:', event.detail.version);
});-
Table of Contents: Headings inside content-switcher blocks do not behave as expected in the table of contents. Quarto's TOC system cannot dynamically update to show only the currently visible version's headings. You should always add
{.unlisted}to headings inside content-switcher blocks to prevent the TOC from displaying headings for all versions simultaneously, which creates a confusing user experience. -
Search Indexing: Quarto's search feature only creates separate search entries for level 2 headings (
##) and above. Level 3 headings (###) and below are included in their parent section's searchable text but won't appear as separate search results.Important: If you want version-specific content blocks to be individually searchable, use level 2 headings (
##) instead of level 3 or lower. If you use{.unlisted}on level 3+ headings, the content will still be searchable but won't appear as a separate search result entry.Example for searchable version blocks:
::: {.content-switcher version="v1.0"} ## Version 1.0 Features {.unlisted} Content here will be searchable as a separate entry. ::: ::: {.content-switcher version="v2.0"} ## Version 2.0 Features {.unlisted} Content here will also be searchable as a separate entry. :::
See the example.qmd file for a complete working example, or view the live demo site.
This extension is designed for HTML output only. When rendering to PDF, Word, or other formats, only the default version content will be included.
The extension consists of three main files:
content-switcher.lua- Pandoc Lua filter for processing contentcontent-switcher.js- Client-side JavaScript for version switchingcontent-switcher.css- Styling for the selector and content blocks
- Quarto >= 1.3.0
[Add your license here]
Contributions are welcome! Please feel free to submit issues or pull requests.