Zola Tutorial 2: Shortcodes
Zola Tutorial 2: Shortcodes
Concept
Zola borrows the idea of shortcodes from WordPress. In Zola, a shortcode is a reusable template fragment that can be used inside Markdown files. Shortcode templates are usually placed under templates/shortcodes.
Shortcodes are often useful in two situations:
- Inserting complex HTML. Markdown is good for writing, but not always convenient for embedding styled HTML.
- Repeating data-driven tasks.
Creating A Shortcode
For example, create youtube.html under templates/shortcodes:
<div {% if class %}class="{{class}}"{% endif %}>
<iframe
src="https://www.youtube.com/embed/{{id}}{% if autoplay %}?autoplay=1{% endif %}"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen>
</iframe>
</div>
This template renders an embedded YouTube video. It has a required id parameter and an optional class parameter. The file name, youtube, becomes the shortcode name, and Markdown files can reference it by that name.
Because Zola may wrap inline HTML nodes such as <a> or <span> into <p>, wrap the fragment in <div> if you do not want that behavior.
Now look at a shortcode implemented as Markdown. Create book.md under templates/shortcodes:
{% set data = load_data(path=path) -%}
{% for book in data.books %}
### {{ book.title }}
{{ book.description | safe }}
{% endfor %}
This book shortcode accepts a path parameter pointing to a .toml file. load_data reads the file, gets the book list, and renders it.
Shortcodes are rendered before Markdown parsing, so they do not know the structure of the final page content. If you need page or section data, Zola provides global functions such as get_page, get_section, get_taxonomy, and get_taxonomy_term. Be careful: these functions are available during zola serve, but not during zola build.
Using Shortcodes
There are two kinds of shortcode:
- Shortcodes without a body, such as the YouTube example above.
- Shortcodes with a body, such as a styled quote block.
For both types, parameters must be named, and all parameters used in the template must be passed. Even if there are no parameters, parentheses are still required after the shortcode name.
Shortcode names and parameter names must follow naming rules: numbers, letters, and underscores. Although parameter names can be numeric, they are usually not practical inside templates.
Parameter values can be:
string: wrapped in double quotes, single quotes, or backticks.bool:trueorfalse.float: floating-point number.integer: integer.array: array containing any type except another array.
If a parameter value has invalid syntax, it is ignored.
Inside a shortcode, you can use page or section depending on where the shortcode is used. You can also use config. These names override passed parameters, so do not use them as parameter names.
Shortcodes Without A Body
For a shortcode without a body, call it like a Tera function. Example usage for the youtube shortcode:
Here is a YouTube video:
{{ youtube(id="dQw4w9WgXcQ") }}
{{ youtube(id="dQw4w9WgXcQ", autoplay=true) }}
An inline {{ youtube(id="dQw4w9WgXcQ", autoplay=true, class="youtube") }} shortcode
If you need to show text that looks like a shortcode inside a document and do not want Zola to render it, use escape syntax such as {{/* and */}} around the example.
Shortcodes With A Body
Suppose we have a quote.html shortcode template:
<blockquote>
{{ body }} <br>
-- {{ author}}
</blockquote>
In Markdown, use it like this:
As someone said:
{% quote(author="Vincent") %}
A quote
{% end %}
The shortcode body is automatically passed into the template as the body parameter.
Shortcodes Without Parameters
Parentheses are required for every shortcode. If a shortcode name is not followed by parentheses, it is rendered as normal text, without warning.
For example, aside.html has no parameters:
<aside>
{{ body }}
</aside>
Use it like this:
Readers can refer to the aside for more information.
{% aside() %}
An aside
{% end %}
Shortcode Context
Besides passed parameters, a shortcode can use several variables.
nth
nth is the number of times this shortcode has been executed in the current Markdown file. For example, with true_statement.html:
<p id="number{{ nth }}">{{ value }} is equal to {{ nth }}.</p>
Usage:
{{ true_statement(value=1) }}
{{ true_statement(value=2) }}
This is useful when implementing note boxes or labels that need automatic numbering.
lang
lang is the current language. It is useful for multilingual sites:

page Or section
Compared with normal templates, shortcodes have fewer available variables. Some variables are empty because Markdown rendering finishes before section processing:
translationsbacklinkspages
One useful page property is colocated_path, which lets you pass a resource name without repeating the full path. For example, it can be used with load_data or resize_image:
{% set resized = resize_image(format="jpg", path=page.colocated_path ~ img_name, width=width, op="fit_width") %}
<img alt="{{ alt }}" src="{{ resized.url | safe }}" />
Examples
YouTube
Parameters
id: the video id, mandatory.playlist: the playlist id, optional.class: a class added to the<div>around the iframe.autoplay: when set totrue, the video autoplays on load.
Template
<div {% if class %}class="{{class}}"{% endif %}>
<iframe src="https://www.youtube-nocookie.com/embed/{{id}}{% if playlist %}?list={{playlist}}{% endif %}{% if autoplay %}?autoplay=1{% endif %}" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>
Usage
{{ youtube(id="dCKeXuVHl1o") }}
{{ youtube(id="dCKeXuVHl1o", playlist="RDdQw4w9WgXcQ") }}
{{ youtube(id="dCKeXuVHl1o", autoplay=true) }}
{{ youtube(id="dCKeXuVHl1o", autoplay=true, class="youtube") }}
Image Gallery
<div>
{% for asset in page.assets -%}
{%- if asset is matching("[.](jpg|png)$") -%}
{% set image = resize_image(path=asset, width=240, height=180) %}
<a href="{{ get_url(path=asset) }}" target="_blank">
<img src="{{ image.url }}" />
</a>
{%- endif %}
{%- endfor %}
</div> Follow ZiCode on WeChat
If this post was useful, you can follow later updates on WeChat as well.
X / Twitter
Follow @ax2_zicode
Faster technical notes, short thoughts, and new-post alerts are posted on X.