Plugins

Turn Docsy’s optional scripts on or off and load your own from site configuration, no layout overrides needed.

Docsy loads some of its optional JavaScript features, and any script you add, as plugins: entries under params.docsy.plugins in your site configuration.

Configure Docsy’s plugins

PluginWhat it doesDefaultLoads onDocs
click-to-copyAdds a copy button to code blocksOn (off under Prism, which has its own)Every pageCopy to clipboard
tabpane-persistRemembers the selected tab across pagesOnEvery page (why)tabpane
markmapRenders markmap code blocks as mind mapsOffPages with a markmap code blockActivating MarkMap support

To turn a plugin off, set its entry to false:

[params.docsy.plugins]
click-to-copy = false
params:
  docsy:
    plugins:
      click-to-copy: false
{
  "params": {
    "docsy": {
      "plugins": { "click-to-copy": 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). Each entry’s fields, types, and defaults:

# Schema of `params.docsy`, the namespace Docsy reserves for theme settings.
type: map
keys:
  plugins:
    type: map # keyed by plugin name; a scalar false turns an entry off
    key:
      pattern: ^[a-z0-9_-]+$
      reservedSuffix: _docsy-shim
    entry:
      enable: { type: bool, default: true }
      defer: { type: bool, default: false } # adds `defer` to the script tag
      pageGate: { type: string, default: '' } # a .Page.Store flag; '' = none
      weight: { type: int, default: 0 } # emission order, ascending, then name
      options: { type: map, default: {} } # reaches the module as @params
  • {} in place of an entry keeps every default.
  • enable is off for false, "false", and 0, and on for any other value; defer is on for true, "true", and 1, and off for any other value. The string forms exist for environment overrides.

Warnings

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

  • An unknown field or a non-map options is ignored and the rest of the entry applies.
  • A name the schema’s pattern rejects or that ends in its reserved suffix, or a scalar entry other than a false spelling, 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 enabled name with no script file (Plugin files) is a different fault: it warns docsy-plugin-missing, gated or not (a disabled entry is never looked up).

Add a custom script

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 = {}
params:
  docsy:
    plugins:
      NAME: {}
{
  "params": {
    "docsy": {
      "plugins": { "NAME": {} }
    }
  }
}

Plugin files

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

FileContract
assets/js/plugins/NAME.jsRequired. Built on its own with js.Build; options reach it as @params.
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.

Companions emit before the script (why). Script and stylesheet tags carry subresource integrity in every environment. Entry keys reach templates and plugin scripts lowercase: .Plugin.pagegate, params.apikey (Configuration § Key spelling).

Security

  • Never pipe .Plugin.options through safeHTML, safeJS, or safeURL in a companion partial: options are site-configured strings, and Hugo’s contextual autoescaping is the defense.
  • In a plugin script, options are values, not markup: set them through DOM and CSSOM properties, never by building HTML or stylesheet text around them (an option interpolated into a <style> can close the rule and open its own).
  • Options, like anything reaching a module as @params, ship world-readable in the built JavaScript: never route secrets through them.
  • Pin third-party dependencies, never latest; vendor build-time fetches and serve them with SRI; and use no loader that pulls unpinned secondary code, which SRI on the loader can’t cover.
  • A plugin that loads remote code gets a pageGate (a flag your own render hook sets with .Page.Store.Set), so its code ships only where used.

Page flags in included content

Some plugins load only on pages that need them: a pageGate names a page flag, and Docsy’s markmap render hook sets one whenever a page has a markmap code block. A flag counts only when it lands on the page that ships.

  • 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 how to clear its gate, see Activating MarkMap support.