Template Variables
Overview
Template variables are placeholders that are replaced by template data. Template variables can be used to populate HTML element attributes, and HTML element contents.
Example
This example shows how variable substitution works in HyperTemplates.
1<head>
2 <title ht-apply>${ site.title } | ${ page.title }</title>
3 <meta ht-apply name='description' content='${ page.description }'>
4 <!-- ...other <head> elements... -->
5</head>
If site.title is HyperTemplates, page.title is Template Variables, and page.description is Learn more about the HyperTemplates variable substitution system., the rendered HTML for this fragment would be as follows:
1<head>
2 <title>HyperTemplates | Template Variables</title>
3 <meta name='description' content='Learn more about the HyperTemplates variable substitution system.'>
4 <!-- other <head> elements -->
5</head>
Specification
Supported elements
Template variables can be used with any HTML element.
See template variable syntax for more information on the template variable CSS syntax, which is required when applying variables to a <style> element or style attribute.
Template variable syntax
Template variables are defined using delimiters.
The default syntax for a template variable is a string that begins with ${ and ends with }.
Example
1<a href='${ page.path }' ht-apply>${ page.title }</a>
An alternate syntax is required for <style> elements and style attributes where the default variable syntax is invalid CSS.
Template variables in a <style> elements or style attributes begin with --ht-var( and end with ).
The --ht-var() syntax also supports CSS-style fallback values as an optional second argument.
Fallback values can be any valid CSS value.
Example
1<head>
2 <!-- other <head> elements -->
3
4 <style id='layout'>
5 :root {
6 --color-1: --ht-var("site.colors.default", rgba(236, 120, 184, 1.0))
7 }
8 </style>
9</head>
In this example the --ht-var( ... ) variable will be replaced with the template data property site.colors.default if it is defined, or else it will use a fallback value of rgba(236, 120, 184, 1.0).
Template variables are not Javascript (or CSS)
The default template variable syntax is inspired by Javascript template literals, but they are not Javascript. However, the syntax for performing "string interpolation" with HyperTemplates variables should look & feel very similar to a Javascript template literal that is only doing variable substitution.
Here's an example of how to perform string interpolation using a Javascript template literal:
1`${ site.title } | ${ page.title }`Note the Javascript-required
`backtick characters.This same string interpolation is possible using HyperTemplates variables, sans backticks:
1<title ht-apply>${ site.title } | ${ page.title }</title>Similarly, the template variable CSS
--ht-var()syntax is inspired by CSS custom functions, but they are not CSS. By adopting the custom function syntax, template variables in<style>elements are valid CSS. If/when a CSS template variable cannot be processed by HyperTemplates (e.g. due to missing template data), they will be present in the generated HTML files and all major browsers will simply ignore them.
String interpolation
String interpolation is the practice of using variables directly inside of a string of text. If you're new to the concept of string interpolation you can think of it almost like a tool for filling in the blanks in a sentence.
Template variables are useful for string interpolation in HTML element attributes and text contents.
Example:
1<link ht-apply rel='stylesheet' href='/css/style.css?version=${ site.version, 1 }'>
In this example we're using a template variable to interpolating, or inserting, a version number in an href URL ?version= query string.
This is a very simple example demonstrating how to configure using URL query parameters for cache busting with HyperTemplates.
Template variable functions
Template variables support functions, and there are three built-in functions: get, text, and markdown.
Functions are invoked from template variables using a function identifier and a list of positional arguments.
The function invocation syntax is identifier(arg1, arg2, arg3).
getfunction- The
getfunction gets template data values.The
getfunction supports a comma-separated list of [template data keys], and one string literal as arguments. String literal values must be quoted, and they must be the last argument passed to thegetfunction. This string literal argument acts as a fallback or default value in case none of the provided template data keys can be resolved.Example
1<title>${ get(page.title, site.title, "Default Title") }</title>This example shows the following arguments:
- A comma-separated list of [template data keys] (i.e.
page.title, site.title) - A string literal default value (i.e.
"Default Title")
- A comma-separated list of [template data keys] (i.e.
textfunction- The
textfunction escapes HTML.Example
If a template data property contains HTML markup that should not be rendered as HTML elements, you will need to use the
textfunction to escape the HTML.For example, if you have a page with the following front matter:
content/index.md 1--- 2created_at: 2026-05-11T11:00:00-07:00 3title: Hyper<Templates> 4---When the resulting
${ page.title }template variable is parsed as HTML it may yield unexpected results:1<!-- fragment --> 2<h1 ht-apply> 3 ${ page.title } 4</h1> 5 6<!-- output --> 7<h1> 8 Hyper<Templates> <!-- browsers will display "Hyper" and ignore the <Templates> tag element --> 9</h1>Use the
textfunction to escape HTML strings:1<!-- fragment --> 2<h1 ht-apply> 3 ${ text(page.title) } 4</h1> 5 6<!-- output --> 7<h1 ht-apply> 8 Hyper<Templates> <!-- browsers will display "Hyper<Templates>" --> 9</h1> markdownfunction- The
markdownfunction converts Markdown strings to HTML elements.Example
If a template data property contains Markdown strings, you may see the markup as plain text.
1<!-- fragment --> 2<div ht-apply> 3 ${ page.overview } 4</div> 5 6<!-- output --> 7<div> 8 ## Hello world 9 10 Lorem ipsum, hipsters get some. 11</div>Use the
markdownfunction to convert Markdown strings to elements:1<!-- fragment --> 2<div ht-apply> 3 ${ markdown(page.overview) } 4</div> 5 6<!-- output --> 7<div> 8 <h2>Hello world</h2> 9 <p>Lorem ipsum, hipsters get some.</p> 10</div>NOTE: the
markdownfunction is available in HTML content template variables, but it is not allowed in HTML attribute variables. - Plugin functions
- Additional functions are added using template variable plugins.
Template variable plugins
HyperTemplates supports custom template variable functions via plugins.
A template variable plugin is created by adding a Javascript file that exports a default function to the site.config.plugins_dir or theme.config.plugins_dir directory.
Examples
The following example creates a plugin called uppercase (created via the plugins/uppercase.js file).
1// uppercase transforms strings to uppercase
2//
3// usage: ${ uppercase(page.title) }
4export default function uppercase(input="") {
5 return input.toUpperCase();
6};
The next example creates a plugin called capitalize (created via the plugins/capitalize.js file).
1// capitalize transforms strings to capitalize
2//
3// usage: ${ capitalize(page.title) }
4export default function capitalize(input="") {
5 return input.charAt(0).toUpperCase() + input.slice(1);
6};
PROTIP: in case you're wondering why we showed two very similar examples here (
capitalizeanduppercase), it's because we wanted to illustrates two important truths about HyperTemplates plugins: they are incredibly simple to create, and in many cases completely unnecessary!You may already be thinking that the
capitalizeexample is not very helpful since CSStext-transform: uppercase;already exists. But did you know that CSS can also perform capitalization using the lesser-known::first-letterpseudo-element (available since 2015)?1[capitalized]::first-letter { 2 text-transform: uppercase; /* only uppercase the first letter */ 3}We love Javascript, but not as much as we love it when we don't need Javascript. 💥
Implicit gets
A template variable that doesn't begin with a registered function name (including plugins) is treated as an "implicit get".
1<!-- this is an implicit get -->
2<h1>${ page.title, "Default Title" }</h1>
3
4<!-- this is a explicit get -->
5<h2>${ get(page.title, "Default Title") }</h2>
Example:
1<a href='${ link.href }' ht-apply>${ link.label, link.title, "Placeholder" }</a>