Changing the Iocaine template
Last updated: 2025-09-07

> Important

> In iocaine 1.x, the templates used handlebars syntax, but iocaine 2.0 uses Jinja2, migration is necessary.

`iocaine` uses minijinja for templating, a template language largely compatible with Jinja2, and will look for a template named `main.jinja` in the configured template directory, or, if no directory is configured, it will fall back to the default template.

# Available variables

Each time a page is rendered, `iocaine` makes data in the following variables available:

    {
      "static_seed": "...",
      "content_type": "text/html",
      "request_host": "example.com",
      "request_uri": "some-path/",
      "params": {
        "q": "some-value"
      }
    }

It is possible to change any of these with `{% set %}`, but that only really makes sense for `content_type` and `static_seed`.

Changing `content_type` changes the `Content-Type` header returned for the request by `iocaine`. While it is possible to set any content type, the templating system requires all output to render into an UTF-8 string, so you’re limited to text-based formats. That still allows rendering SVGs, Atom or RSS feeds, sitemaps, CSS stylesheets, JavaScript stuff, and those kind of things.

Changing `static_seed` changes the random number generator, but only if it is set before calling any other custom function. It is recommended not to change it, unless you are absolutely sure you know what you are doing. It can be used to set the seed to that of a different page, like that of the containing directory of an SVG image.

# Custom functions

To do its job properly, `iocaine` relies on the template to drive how garbage is generated, and how much of it. To facilitate that, a number of custom functions are provided. These aren’t context-aware, so may end up generating text that is invalid in certain contexts: quotation marks when generating links, HTML elements whene generating garbage. It is recommended to escape them using the engine-provided `escape` and `urlencode` filters (the latter is available since iocaine 2.5.0), as appropriate.

`rand(max=N, min=1, pos=<automatic>, group="default")`

  Generates a random number between `min` and `max`, seeded by `pos` and `group`. Only the `max` named argument is required, the rest have sensible defaults. If one wants to generate the same random number as elsewhere, use the same parameters, including `pos` and `group`.

  Every time `rand()` is called without explicitly giving it a `pos` argument, the automatically tracked variable gets incremented. This guarantees that any calls to `rand()` that weren’t explicitly set up to give the same answer as another one, will choose a random answer.

  All other functions that need random numbers use this framework, and work in exactly the same way.

  Examples:

      {{ rand(max=15) }} == {{ rand(min=1, max=15, pos=0, group="default") }}
      {{ rand(max=15) }} is not guaranteed to be the same as {{ rand(max=15, pos=0) }}!

      Generate 1-5 paragraphs of markov-based garbage:

      {% for p in range(rand(max=5, group="paragraphs")) %}
      <p>{{ markov(min=10, max=128) }}</p>
      {% endfor %}

`markov_gen(max=N, min=1, words=None, pos=<automatic>, group="markov")`

  Generates either `words` number of words of garbage (if a `words` argument was given), or a random number of words between `min` and `max`. Only the `max` argument is mandatory, the rest have sensible defaults.

  Examples:

      {{ markov_gen(min=10, max=128) | escape }} generates 10-128 words of garbage.
      {{ markov_gen(words=4) | escape }} always generates 4 words of garbage.

      {{ markov_gen(max=128, pos=42, group="title") | escape }}
       - This will always generate the same garbage.

`href_gen(max=N, min=1, words=None, pos=<automatic>, group="href")`

  Generates a string suitable to be included in a `href`, made up of either `words` number of words, or a random number between `min` and `max`. The words are joined together by a dash (`-`). Only the `max` argument is mandatory, the rest have sensible defaults.

  Examples:

      <ul>
       {% for l in range(rand(max=5, group="links")) %}
       <li>
        <a href="{{ href_gen(max=2) | urlencode }}">
         {{ markov_gen(min=2, max=7, group="href") | escape }}
        </a>
       </li>
       {% endfor %}
      </ul>

`regex_gen(PATTERN, max=N, min=1, pos=<automatic>, group="regex")`

  Generates a string that matches the regexp pattern given in `PATTERN`. `N` controls the maximum number of repeats in patterns like `x*`, `x+`, or `x{n,}`. Both `PATTERN` and the `max` argument are mandatory.

  This function can be used to generate URL structures that match a pattern, or “secrets”, or a whole lot of other things the author of this documentation has not thought of.

  Examples:

      {% set username = regex_gen("[-\\.a-zA-Z0-9]{3,32}", max=32) %}
      <a href="/@{{ username | urlencode }}/">@{{ username | escape }}</a>

  If you need to use a backslash-escape in the regexp, that will need to be doubly-escaped, as shown above.

`qr(CONTENT, format="png", width=<automatic>, height=<automatic>)`

  Generates a QR code of `CONTENT`, in a given `format` and `width`x`height` dimensions. If specifying dimensions, both of them must be specified, otherwise they will be silently ignored. The supported `format`s are: `png`, `svg`, and `svg:raw`. The first two generates a base64-encoded `data:` url that can be directly used by an `<img>` tag, the latter, `svg:raw` outputs the generated SVG as-is, and can be included directly in the template, as-is.

  Examples:

      <img src="{{ qr(markov_gen(max=7, group="qr")) }}"
           alt="{{ markov_gen(min=3, max=15, group="qr") | escape }}">

`parse_file(PATH[, format="<json|yaml>"])`

  Parse the given `PATH` as either `json` or `yaml`, and return the parsed structure. Intended to be used together with `{% set %}`, to load data from a separate file.

  Examples:

      {% set secrets = parse_file("secret-rules.yaml", format = "yaml") %}
      {%- with rule = secrets["github-pat"],
               name = regex_gen(rule.name_regex, max=256)|upper|replace("-", "_"),
               value = regex_gen(rule.secret_regex, max=256) -%}
      {{ name }}="{{ value }}"
      {%- endwith -%}

  > Added in iocaine 2.1.0.

# Custom filters

Apart from the functions described above, `iocaine` also introduces the following filters for the templates:

`STRING | matches(REGEXP)`

  Allows matching a string against a regexp, which can be useful to drive the template to produce more realistic content, based on the `request_uri`, for example.

  Examples:

      {% if request_uri|matches("^/blog/") %}
      {% include "blog.jinja" %}
      {% endif %}

`STRING | sanitize_filename(replacement="")`

  Sanitizes an input string to become a filename that is safe to include. This is meant to make it possible to provide per-host templates:

      {% include ("hosts/" ~ request_host|sanitize_filename ~ ".jinja") ignore missing %}