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 plugin, which can accept one or more positional arguments. This plugin converts string or int values into ISO8601 duration strings. value PT1H1M1S.

plugins/duration_iso.js
 1// plugins/duration_iso.js
 2//
 3// duration_iso converts a duration in seconds into an ISO 8601 duration string.
 4// 
 5// usage:
 6//   duration_iso(3661)   # PT1H1M1S
 7//   duration_iso("3661") # PT1H1M1S
 8//   duration_iso(0)      # PT0S
 9export default function durationISO(input) {
10  const seconds = Math.max(0, Math.round(Number(input) || 0))
11  const hours = Math.floor(seconds / 3600)
12  const minutes = Math.floor((seconds % 3600) / 60)
13  const remainder = seconds % 60
14  const parts = []
15
16  if (hours) parts.push(`${hours}H`)
17  if (minutes) parts.push(`${minutes}M`)
18  if (remainder || parts.length === 0) {
19    parts.push(`${remainder}S`)
20  }
21
22  return `PT${parts.join("")}`
23}

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. Plugin identifiers are then bound to the default function exported by the plugin file.

Example

In the following example, the plugin file at plugins/duration_iso.js binds the identifier duration_iso to the function returned by export default function (i.e. durationISO).

plugins/duration_iso.js
 1// plugins/duration_iso.js
 2//
 3// duration_iso converts a duration in seconds into an ISO 8601 duration string.
 4// 
 5// usage:
 6//   duration_iso(3661)   # PT1H1M1S
 7//   duration_iso("3661") # PT1H1M1S
 8//   duration_iso(0)      # PT0S
 9export default function durationISO(input) {
10  const seconds = Math.max(0, Math.round(Number(input) || 0))
11  const hours = Math.floor(seconds / 3600)
12  const minutes = Math.floor((seconds % 3600) / 60)
13  const remainder = seconds % 60
14  return isoString(hours, minutes, remainder)
15}
16
17function isoString(hours, minutes, remainder) {
18  const parts = []
19  if (hours) parts.push(`${hours}H`)
20  if (minutes) parts.push(`${minutes}M`)
21  if (remainder || parts.length === 0) {
22    parts.push(`${remainder}S`)
23  }
24  return `PT${parts.join("")}`
25}

NOTE: plugins may define multiple functions, including internal helper functions (see isoString above), but only one export default function.

Plugin arguments


Plugins can accept zero or more 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.

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};

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.

💬 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! 🎨