Plugins
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
| Plugin | What it does (Default / Loads on) | Learn more |
|---|---|---|
click-to-copy | Adds a copy button to code blocks (On, but off under Prism, which has its own / Every page) | Copy to clipboard |
tabpane-persist | Remembers the selected tab across pages (On / Every page (why)) | tabpane |
markmap | Renders markmap code blocks as mind maps (Off / Pages with a markmap code block) | Activating MarkMap support |
To turn a plugin off, set its enable field to false:
[params.docsy.plugins.click-to-copy]
enable = falseparams:
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:
'defer': { type: bool, default: false } # coerced; adds `defer` to the script tag
'enable': { type: bool, required: true } # coerced
'options': { type: map } # reaches the module as @params
'version':
type: string # coerced
pattern: ^[0-9A-Za-z.+-]+$
'weight':
type: int # coerced
- Fields are optional unless marked
required: true. {}for a theme plugin keeps every inherited field, includingenable.enableis off forfalse,"false", and0, and on for any other value;deferis on fortrue,"true", and1, and off for any other value. The string forms exist for environment overrides.weightcontrols emission order within the plugin registry:- Lower values emit first. Omitted weight and explicit
0form the normal group; negative values precede it, positive values follow it. - Use distinct weights when order matters; equal-weight order is unspecified.
- Weight does not override
deferor wait for asynchronous initialization. Use the dependency’s readiness mechanism when needed.
- Lower values emit first. Omitted weight and explicit
For guidance on using version, see
Dependency versions.
Warnings
Every registry shape warning carries the id docsy-config (to silence one, see
Configuration § Configuration warnings):
- An unknown field or a non-map
optionsis ignored and the rest of the entry 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.docsyorparams.docsy.pluginsthat is not a map empties the registry, Docsy’s own plugins and their deprecated aliases included.plugins: {}keeps them; a valuelessplugins: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) 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. An empty or
malformed value 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
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.
- Save the script as
assets/js/plugins/NAME.js, withNAMEin lowercase. - Register it under
params.docsy.plugins(configuration reference):
[params.docsy.plugins.NAME]
enable = trueparams:
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.
| File | Contract |
|---|---|
assets/js/plugins/NAME.js | Required. Built on its own with js.Build; options reach it as @params. |
layouts/_partials/scripts/plugins/NAME.html | Optional companion partial for vendored libraries, markup, or configuration; receives (dict "Page" PAGE "Plugin" ENTRY). |
assets/scss/plugins/NAME.scss | Optional companion stylesheet, through the Sass pipeline. |
layouts/_partials/scripts/plugins/NAME_docsy-shim.html | Optional 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 and plugin scripts lowercase: an option apiKey is
params.apikey in the script (Configuration § Key spelling).
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. Two of Docsy’s
plugins ship a shim, markmap and click-to-copy: your file replaces it, gate,
Prism guard, and deprecated-parameter handling included, 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
The entry’s version selects a plugin dependency version. The companion partial
determines which dependency it refers to. The field does not automatically
identify the version of the plugin script itself or the Docsy theme.
For a custom plugin with a configurable dependency, set version on its
registry entry 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.
Unlike options, the entry’s version is not passed to the plugin script
through @params. For a working example and instructions for overriding a
theme-provided pin, see MarkMap version.
Security
- Never pipe
.Plugin.optionsthroughsafeHTML,safeJS, orsafeURLin 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 on the entry’s
version, neverlatest. - 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
markmapblock in content pulled in through.RenderShortcodesflags 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
.Contentflags 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.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.