Plugins

Turn Docsy’s optional scripts on or off, and configure them, from site configuration.

Docsy loads some of its optional JavaScript features as plugins: entries under params.docsy.plugins in your site configuration.

Configure Docsy’s plugins

PluginWhat it does (Default / Loads on)Learn more
click-to-copyAdds a copy button to code blocks (On, but off under Prism, which has its own / Every page)Copy to clipboard
tabpane-persistRemembers the selected tab across pages (On / Every page (why))tabpane
markmapRenders markmap code blocks as mind maps (Off / Pages with a markmap code block)Activating MarkMap support
mermaidRenders mermaid code blocks as diagrams (On / Pages with a mermaid code block)Diagrams with Mermaid

To turn a plugin off, set its enable field to false:

[params.docsy.plugins.click-to-copy]
enable = false
params:
  docsy:
    plugins:
      click-to-copy:
        enable: false
{
  "params": {
    "docsy": {
      "plugins": { "click-to-copy": { "enable": false } }
    }
  }
}

Configuration reference

Docsy’s own plugins are declared in the theme’s hugo.yaml; your entries merge over them by name and field (Configuration § Theme defaults). The schema defines each entry’s keys, required fields, types, defaults, and syntactic patterns:

type: map
entries:
  'plugins':
    type: map # nonempty
    key: # plugin name
      type: string # coerced, lowercased
      pattern: ^[a-z0-9_-]+$
      reservedSuffix: _docsy-shim
    value:
      type: map
      entries:
        'enable': { type: bool, required: true } # coerced
        'version':
          type: string # coerced
          pattern: ^[0-9A-Za-z.+-]+$
        'options': { type: string } # plugin-defined format; see the plugin's guide
        '_defer': { type: bool, default: false } # coerced; plugin-author field: adds `defer` to the script tag
  • Fields are optional unless marked required: true.
  • {} for a theme plugin keeps every inherited field, including enable.
  • Language-specific params apply, so an entry can differ per language.
  • enable is off for false, "false", and 0, and on for any other value. The string forms exist for environment overrides.
  • version selects the version of a plugin’s dependency, not of the plugin script or of Docsy. To override a theme plugin’s pin, see Mermaid version or MarkMap version.
  • options holds a plugin’s own settings, as a string whose format and validation are the plugin’s; Docsy passes the value to the plugin unchanged. For the shape a theme plugin takes, see its guide (Mermaid settings).
  • _defer is the plugin author’s field (the _ prefix marks such fields), declared with the plugin (Loading strategy (experimental)); leave it alone on a plugin you didn’t write.

Warnings

Every registry shape warning carries the id docsy-config (to silence one, see Configuration § Configuration warnings):

  • An unknown field is ignored and the rest of the entry applies.
  • An unknown key directly under params.docsy is ignored and the rest of the map applies.
  • A name the schema’s pattern rejects or that ends in its reserved suffix, a scalar entry, or an entry missing a required field drops the whole entry.
  • A params.docsy or params.docsy.plugins that is not a map empties the registry, Docsy’s own plugins and their deprecated aliases included. plugins: {} keeps them; a valueless plugins: is null and drops them.
  • An empty registry after configuration merging warns; a registry with all entries disabled is valid.
  • An enabled name with no script file (Plugin files (experimental)) is a different fault: it warns docsy-plugin-missing (a disabled entry is never looked up).

version validation applies to entries not already dropped by the shape guards, including disabled entries. An exact X.Y.Z passes without a version warning; another value matching the schema’s pattern, such as latest, warns under NAME-floating-version, where NAME is the entry’s name. A value outside the pattern, such as a range or operator (^11, >=11, *) or an empty or malformed string, fails the build and skips the entry before its companion runs.

For why Docsy pins versions, see Pinned script-dependency versions.

Add a custom script

EXPERIMENTAL

This section is experimental; configuring Docsy’s plugins is supported.

For a script that should load at the end of every page, register it as a plugin; for markup in <head>, inline snippets, or third-party tags, use the head and body hooks instead.

  1. Save the script as assets/js/plugins/NAME.js, with NAME in lowercase.
  2. Register it under params.docsy.plugins (configuration reference):
[params.docsy.plugins.NAME]
enable = true
params:
  docsy:
    plugins:
      NAME:
        enable: true
{
  "params": {
    "docsy": {
      "plugins": { "NAME": { "enable": true } }
    }
  }
}

Plugin files

A project file shadows the theme’s of the same name, which is how you replace one of Docsy’s plugins, its companions, or its shim.

FileContract
assets/js/plugins/NAME.jsRequired. Built on its own with js.Build.
layouts/_partials/scripts/plugins/NAME.htmlOptional companion partial for vendored libraries, markup, or configuration; receives (dict "Page" PAGE "Plugin" ENTRY).
assets/scss/plugins/NAME.scssOptional companion stylesheet, through the Sass pipeline.
layouts/_partials/scripts/plugins/NAME_docsy-shim.htmlOptional shim partial; adjust a plugin per page.

Companions emit before the script (why). Script and stylesheet tags carry subresource integrity in every environment. Entry keys reach templates lowercase (Configuration § Key spelling).

Loading strategy

A plugin’s script runs synchronously by default. For a script that scans the document once when it runs, set _defer: true. The script then runs after parsing and sees markup emitted after its tag, including the body-end hook, as click-to-copy does. Declare _defer where you register the plugin, or set it in its shim.

Adjust a plugin per page

A shim adjusts a plugin’s registry entry for each page before the plugin loads. Add one for your own plugin, or for one of Docsy’s. Three of Docsy’s plugins ship a shim, mermaid, markmap, and click-to-copy: your file replaces that plugin’s shim and everything it does (shim contract), so start from a copy of the theme’s file, in scripts/plugins/.

Create layouts/_partials/scripts/plugins/NAME_docsy-shim.html, with the plugin’s registry name as NAME (shim contract):

{{ $entry := .Plugin -}}
{{ if not (.Page.Store.Get "hasMyFeature") -}}
  {{ $entry = merge $entry (dict "enable" false) -}}
{{ end -}}
{{ return $entry -}}

That shim loads the plugin only on pages that use it: a render hook of yours sets the flag with .Page.Store.Set where the feature’s markup appears. Before relying on a flag, read Page flags in included content.

Dependency versions

For a custom plugin with a configurable dependency, set version on its registry entry (configuration reference) and read .Plugin.version in the companion partial. Use that value to select the dependency’s code, for example in a build-time fetch URL. Declaring version does not fetch code automatically. Omit the field if the plugin has no dependency version to configure.

The entry’s version is not passed to the plugin script. For a working example, see the markmap companion in scripts/plugins/.

Plugin settings

A plugin reads its settings from its entry’s options (configuration reference); the shim receives the value as the site wrote it and may decode it before the companion runs. Choose the string’s format and document it with the plugin. It is a string because Hugo lowercases map keys (why); Docsy’s plugins take a JSON object, decoded at build time with transform.Unmarshal, or in the browser with JSON.parse after the companion emits it. For the pattern, see the mermaid shim and companion in scripts/plugins/ (shim contract).

Security

  • Pin third-party dependencies on the entry’s version, never latest.
  • Vendor build-time fetches and serve them with SRI.
  • Use no loader that pulls unpinned secondary code, which SRI on the loader can’t cover.
  • Load remote code only on pages that use it: gate the plugin with a shim.

Page flags in included content

Some plugins load only on pages that need them: Docsy’s markmap render hook sets a page flag whenever a page has a markmap code block, and the plugin ships where the flag is set. A flag counts only when it lands on the page whose output the plugin is emitted into.

  • A render hook runs in the context of the page being rendered, so a markmap block in content pulled in through .RenderShortcodes flags the page that includes it.
  • A shortcode runs in the context of the page whose file contains it, so a shortcode in included content would flag the included page, and the including page would never see the flag.
  • Content pulled in through .Content flags the included page in both cases.

That is why Docsy ships tabpane-persist ungated, on every page: tabpanes come from a shortcode. For MarkMap’s authoring paths and the remedy, see When a MarkMap doesn’t render.