Template Plugins

Overview


Plugins extend the capabilities of HyperTemplates with support for Javascript functions. A plugin is a Javascript file that exports a default function. The exported function can accept positional arguments supplied at run time. Some plugins get predefined local bindings injected into the runtime environment (e.g. local const declarations). All plugins are executed in an Immediately Invoked Function Expression (IIFE) for scope isolation.

NEW: Template plugins are available in hyperctl version 0.20.0 and newer.

Examples


This is an example template variable plugins, which can accept one or more positional arguments.

plugins/uppercase.js
1// uppercase transforms strings to uppercase
2export default function uppercase(input="") {
3    return input.toUpperCase()
4};

This is an example computed namespace plugin, which does not accept arguments but does have access local data bindings, including: ht.*, build.*, env.*, data.*, theme.*, and site.* template data (including the full site.pages sitemap):

data/tags.js
 1// tags.js generates a data.tags object of unique website tags w/ tag counts
 2// example output: {"html":{"count":3,"label":"HTML"},"css":{"count":1,"label":"CSS"},"rss":{"count":4,"label":"RSS"}}
 3export default function tagcloud() {
 4    var result = {}
 5    for (let page of site.pages) {
 6        let tags = page.tags || []
 7        console.log(`${ page.path } has ${ tags.length } tags`)
 8        for (let tag of tags) {
 9            let id = tag.toLowerCase();
10            result[id] = (result[id] || { label: tag, count: 0 })
11            result[id].count += 1
12        }
13    }
14    return result
15};

Specification


Plugins kinds


HyperTemplates currently supports two kinds of plugins:

Template variable plugins
Add custom template variable functions with template variable plugins. Learn more
Computed namespace plugins
Add generated template data with computed namespace plugins. Learn more

Plugin identifiers


HyperTemplates plugin identifiers are derived via file name.

For example, computed namespace plugin at <data_dir>/archive.js is named archive.

Plugin runtime environment


HyperTemplates plugins run in sandboxed JavaScript environments that support modern JavaScript: let, const, arrow functions, template literals, destructuring, classes, Promise, and async/await, on top of an ECMAScript 5.1+ baseline.

Think of it as "JavaScript the programming language" without "JavaScript the browser" or Node.js — there is no DOM (window, document, fetch) and none of the Node.js globals (require, process, Buffer, setTimeout).

Plugins can leverage plain functions, core JavaScript types like objects and arrays, plus standard built-ins including Object, Array, String, Number, Boolean, BigInt, Symbol, Date, RegExp, Map, Set, Promise, JSON, Math, Proxy, Reflect, undefined, Error, TypeError, RangeError, SyntaxError, ReferenceError, parseInt, parseFloat, NaN, isNaN, Infinity, isFinite, encodeURI, decodeURI, encodeURIComponent, decodeURIComponent, and more.

Plugins are executed inside Immediately Invoked Function Expressions (IIFEs), so each invocation runs in an isolated scope with no shared state.

Plugin logging


HyperTemplates plugins have access to a console object, including console.log, console.info, console.warn, console.error, and console.debug. All console.* methods write to standard output with a [scripting] prefix. A console.logger(name) function returns a console-shaped object that prefixes its output with the provided name (i.e. [name] ) which is useful for debugging output from a specific plugin.

Examples

In the following example we use console.log() directly.

The highlighted line will generate output like [scripting] /about/ has 4 tags.

data/tags.js
 1// tags.js generates a data.tags object of unique website tags w/ tag counts
 2// example output: {"html":{"count":3,"label":"HTML"},"css":{"count":1,"label":"CSS"},"rss":{"count":4,"label":"RSS"}}
 3export default function tagcloud() {
 4    var result = {}
 5    for (let page of site.pages) {
 6        let tags = page.tags || []
 7        console.log(`${ page.path } has ${ tags.length } tags`)
 8        for (let tag of tags) {
 9            let id = tag.toLowerCase();
10            result[id] = (result[id] || { label: tag, count: 0 })
11            result[id].count += 1
12        }
13    }
14    return result
15};

In this example we use console.logger("tags.js") to customize the log output.

The resulting call to logger.log() will generate output like [tags.js] /about/ has 4 tags.

data/tags.js
 1// tags.js generates a data.tags object of unique website tags w/ tag counts
 2// example output: {"html":{"count":3,"label":"HTML"},"css":{"count":1,"label":"CSS"},"rss":{"count":4,"label":"RSS"}}
 3const logger = console.logger("tags.js");
 4export default function tagcloud() {
 5    var result = {}
 6    for (let page of site.pages) {
 7        let tags = page.tags || []
 8        logger.log(`${ page.path } has ${ tags.length } tags`)
 9        for (let tag of tags) {
10            let id = tag.toLowerCase();
11            result[id] = (result[id] || { label: tag, count: 0 })
12            result[id].count += 1
13        }
14    }
15    return result
16};

Positional arguments


Some plugin kinds support positional arguments.

Example

For example, this template variable plugin accepts two positional arguments, date and format:

plugins/datefmt.js
 1// datefmt formats RFC3339 date strings using the Unicode LDML microsyntax for dates.
 2// 
 3// Date Field Symbol Table: https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
 4//
 5// Format tokens:
 6//
 7//   yyyy   4-digit year         (2026)
 8//   yy     2-digit year         (26)
 9//   MMMM   Full month name      (April)
10//   MMM    Abbreviated month    (Apr)
11//   MM     2-digit month        (04)
12//   M      Numeric month        (4)
13//   dd     2-digit day          (12)
14//   d      Numeric day          (12)
15//   EEEE   Full weekday         (Sunday)
16//   EEE    Abbreviated weekday  (Sun)
17//   HH     24-hour, padded      (14)
18//   hh     12-hour, padded      (02)
19//   mm     Minute, padded       (30)
20//   ss     Second, padded       (05)
21//   a      AM/PM marker         (PM)
22//   ZZZZZ  ISO 8601 w/ colon    (-07:00)
23//   Z      ISO 8601 w/o colon   (-0700)
24//
25// Usage: ${ datefmt page.created_at "EEEE, MMMM d, yyyy" }
26export default function datefmt(date="", format="") {
27    const datetime = new Date(date);
28    var out = ""
29    // apply formatting
30    return out
31}

If used in a template variable like ${ datefmt page.created_at "EEEE, MMMM d, yyyy" }, HyperTemplates will lookup the value of page.created_at (an RFC3339 string), and call datefmt(...["2026-05-08T11:00:00-07:00", "EEEE, MMMM d, yyyy"]).

When used in a template variable like ${ datefmt page.created_at "EEEE, MMMM d, yyyy" }, HyperTemplates resolves page.created_at from template data and invokes the plugin with the resulting value (an RFC3339 string), in the same order: datefmt("2026-05-08T11:00:00-07:00", "EEEE, MMMM d, yyyy").

Local variables


HyperTemplates plugins are executed inside Immediately Invoked Function Expressions (IIFEs) with optional predefined local const bindings. For example, computed namespace plugins have access to ht, env, build, theme, site, and data as local variables.

💬 Join the community

Stay up-to-date with the latest releases and other news from the Team behind HyperTemplates. Ask the developers questions, get help from the community, and share your creations! 🎨