Skip to main content

Distributing Extensions

Overview

Quarto extensions are directories that contain an _extensions sub-directory with one or more extensions. The files above the _extensions directory are not installed, so typically contain README and LICENSE files, examples, test cases, etc.

There are two distinct ways to distribute extensions to end users:

  1. Publish your extension in a public GitHub repository.

  2. Bundle your extension into a .zip or .tar.gz archive.

Each method has benefits and drawbacks that will be explored below. First we’ll cover the basic file structure and contents of an extension.

Extension Contents

Quarto Extensions are directories that contain an _extensions folder that contains one or more extension contributions. While the most common case is the distribution of a single extension, it is possible to create a single extension directory that includes multiple shortcodes, multiple filters, or a combination of both.

Here is the contents of an extension named my-filter:

README.md
LICENSE
example.qmd
_extensions/
  my-filter/
    _extension.yml
    my-filter.lua

Note that the only thing strictly required is the _extensions directory (anything above that is for your own purposes and is ignored during installation). Even so, it’s good practice to include a README.md and LICENSE file, and the example.qmd will be useful for developing your extension.

Place each extension directly under _extensions, named after the extension (_extensions/my-filter/). Don’t add a folder for your GitHub organization (_extensions/cooltools/my-filter/). For installations from GitHub, Quarto adds the owner folder (see Installed Layout). Any owner folder in the source is ignored. To include an extension from another owner, embed it in your extension as described in Embedding Extensions.

NotePre-release Feature

This feature is new in the upcoming Quarto 1.11 release. To use the feature now, you’ll need to download and install the Quarto pre-release.

When an extension’s source has an owner folder that differs from the owner it is installed under, quarto add prints a note with the name to use for the installed extension.

_extension.yml

Each extension is defined by its _extension.yml file which contains the metadata about the extension as well as the what items it contributes when used. For example, here is the _extension.yml for a filter extension:

title: My Filter
author: Cooltools
version: 1.0.0
quarto-required: ">=1.2.0"
contributes:
  filters:
    - my-filter.lua

Here are all of the fields that can be specified in the _extension.yml file:

title

The extension’s name

author

The author of the extension

version

A semantic version number this release. When installing, updating, or releasing an extension, this version number will be used to present a summary of actions to the user.

quarto-required

A semantic version number indicating the minimum quarto version required to run this extension.

contributes

The items that this extension will contribute to the render. These are allowed subkeys:

shortcodes

A list of shortcode files that should be loaded when this extension is installed.

filters

A list of filters that should be loaded when this extension is included in the list of filters used to render a document or project. The order of the filters in this list will be preserved.

formats

A record containing the key value pairs of output formats and the metadata associated with that output format.

project

Information about a custom Quarto project type.

revealjs-plugins

Information about a RevealJS plugin.

metadata

Metadata to be merged into the Quarto project using the extension.

brand

Information about a brand extension.

engine

Information about a custom engine.

Note

The version field is checked for conformity to semantic versioning. For example, valid version values would be:

  • 1.0.0
  • 2.3.4-alpha
  • 1.2.3-beta+build567
  • 3.0.0-alpha.2
  • 4.2.1+build.987

Invalid version values would be:

  • 1.2 (missing patch version)
  • 1.2.3.4 (extra version segment)
  • 1.2.3-beta.5+build (missing build metadata identifier)

Installed Layout

When an extension is installed, Quarto copies each extension found in the source _extensions directory into the _extensions directory of the project or document. Extensions installed from GitHub land in a folder named after the GitHub owner they were installed from. Extensions installed from any other source have no owner and land directly in _extensions:

Command Installed location
quarto add quarto-ext/lightbox _extensions/quarto-ext/lightbox/
quarto add https://github.com/quarto-ext/lightbox/archive/refs/heads/main.zip _extensions/quarto-ext/lightbox/
quarto add https://cooltools.org/quarto/shorty.zip _extensions/shorty/
quarto add ~/Downloads/shorty.zip _extensions/shorty/
quarto add /share/quarto/extensions/shorty _extensions/shorty/

The owner comes from the source you install from, not from the extension’s source files. The extension folder keeps the name it has under the source _extensions directory, which can differ from the repository name. For example, quarto add cooltools/icons/fontawesome (see subdirectories below) installs _extensions/cooltools/fontawesome/.

GitHub Distribution

Distributing extensions on GitHub has a number of benefits, including compact syntax (e.g. quarto add org-name/extension), the use of organizations as a “namespace” for managing name conflicts, and the ability to target specific releases or tags.

For example, the extensions in the quarto-ext GitHub organization can be added to a project with these commands:

Terminal
quarto add quarto-ext/lightbox
quarto add quarto-ext/fontawesome

By default, extensions are added from the HEAD of the main branch of the repository. You can also target tags and/or branches in your repository by including an @ after the repository name. For example:

Terminal
quarto add quarto-ext/lightbox@v1.2
quarto add quarto-ext/lightbox@bugfix-22

Extensions added from GitHub have another special property: the GitHub organization can be used as a namespace qualifier to disambiguate extensions that have the same name. For example, if you have two different lightbox extensions in your project, you explicitly specify the quarto-ext one as follows:

---
filters:
  - quarto-ext/lightbox
---

A qualified name <owner>/<name> refers to the extension installed in _extensions/<owner>/<name>/. You can use it wherever you refer to an extension by name, for example in filters or in a format name such as format: quarto-journals/acm-pdf. A name without an owner (lightbox, acm-pdf) matches the extension whether or not it was installed with an owner, and Quarto warns if more than one installed extension matches.

Because the owner depends on where an extension is installed from, use the name without an owner in a template.qmd that ships with your extension (for example format: acm-pdf). The template then works whether it is used from GitHub, from an archive, or from a local copy.

You can also add an extension from a subdirectory of a GitHub repository. For example, here we install two different extensions from the cooltools/icons repository:

Terminal
quarto add cooltools/icons/fontawesome
quarto add cooltools/icons/iconify

Archive Distribution

Distributing extensions as a .zip or .tar.gz archive has the benefit of not requiring public distribution. These extensions can also be added directly from non-GitHub version control services using the archive URLs normally provided for repositories.

Note that unlike GitHub hosted extensions, extensions installed from archives or local directories do not have an organizational namespace: they install as _extensions/<name>/, all share a single namespace, and are referred to by name only. The one exception is a GitHub archive URL (https://github.com/<owner>/<repo>/archive/refs/...), which installs under its GitHub owner like any other GitHub extension.

Git Repositories

To add an extension to a project from a GitLab repository you could do this:

Terminal
quarto add https://gitlab.com/cooltools/shorty/-/archive/main/shorty-main.zip

You’ll note that the above URL references the main branch. You can similarly target any other branch, tag, or release. For example, to add an extension using the v1.0 tag:

Terminal
quarto add https://gitlab.com/cooltools/shorty/-/archive/v1.0/shorty-main.zip

If you are using BitBucket, Azure DevOps, or another Git hosting provider, consult the appropriate service documentation to learn how to form archive URLs for repositories.

Archive Files

The above examples demonstrate adding an extension from a Git repository, you can also add an extension from an archive published to an ordinary web host. For example:

Terminal
quarto add https://cooltools.org/quarto/shorty.zip

Or alternatively from a local archive file or even ordinary uncompressed directory:

Terminal
quarto add ~/Downloads/shorty.zip
quarto add /share/quarto/extensions/shorty

Embedding Extensions

A format extension can use filters and shortcodes from another extension. If that extension comes from another owner, embed it in your format extension. Extensions placed alongside your format extension in _extensions are installed under the same owner as your format extension (see Installed Layout).

To embed an extension, run quarto add with the --embed option from the directory that contains your _extensions directory, giving the name of your format extension:

Terminal
quarto add quarto-ext/fancy-text --embed my-format

This installs the extension inside your format extension, with its own owner:

_extensions/
  my-format/
    _extension.yml
    _extensions/
      quarto-ext/
        fancy-text/

Embedded extensions keep this layout when your extension is installed. Once the extension is embedded, refer to it in your _extension.yml by its name or qualified name:

_extensions/my-format/_extension.yml
contributes:
  formats:
    html:
      shortcodes:
        - quarto-ext/fancy-text

Only extensions that contribute a format can embed other extensions. Run quarto add --embed before adding the embedded extension’s name to _extension.yml. Quarto reads this file during embedding and fails if it references an extension that is not yet embedded.