elf-notes/content/docs/zola/_index.md

6.2 KiB

+++ title = "Zola" description = "Zola is a static site generator" date = 2025-05-01T18:00:00+00:00 updated = 2021-05-01T18:00:00+00:00 template = "docs/section.html" sort_by = "weight" weight = 4 draft = false +++

Zola

Zola is a static site generator written in Rust. It builds an entire web page out of a structured markdown collection and a collection of HTML templates. The templating language is called Tera.

Running Zola

Zola is a single binary.

  • zola init - Starts a new Zola project. Creates the structure described in the next section, especially if you enable sass.
  • zola serve - Starts a localhost webserver to serve your content for demo.
  • zola build - Builds a Zola project into a collection of static HTML.

Project layout

The following files and folders define a basic Zola project

  • config.toml: A configuration file in the TOML configuration language. Can contain a lot of information that will be made available to all pages in the system.
  • content/: The structured content folder.
  • sass/: Sass is a CSS preprocessor language that makes CSS easier.
  • static/: Static content such as images, fonts, and Javascript
  • templates: The HTML templates (and includes, etc) that define the website's layouts

Note that if you want to use modern Javascript features such as a precompiler like Typescript or a bundler like Webpack, that is outside the functionality of Zola. You'll have to have a separate project for that, or a subfolder or git submodule, and you'll have to have a separate build step for that.

It's also a bit of a shame that Tera is 100% an HTML play, and there's no HAML or similar HTML templating involved. That would be a nifty project.

Tera

The Tera templating language looks a lot like Django's, or for that matter any number of templating languages. Every page generated with Tera receives a context, which can be thought of as a JSON object with which to populate the page (it is, technically, a Serde::Serializable). Zola provides a large and complex context, which is explained further down.

Tera has the following syntax. Everything within these markers is processed by Tera; everything outside is left untouched.

  • {{ and }} for expressions
  • {% and %} for statements
  • {# and #} for comments

Statements that have a dash as part of their delimeter remove all whitespace from around the delimiter. {%- says to eliminate all whitespace between this statement and anything that came before it; -%} says to eliminate everything that comes after it.

Statements

block

The block is probably the most important statement; it defines a chunk of content. When a template is created, it can define its whole content as a block, and it can define subsections (note: section is a heavily overloaded word) of itself. These blocks can be used as-is, or replaced when another template inherits them and overwrites them.

set

Variables are set in a given scope with set:

{% set my_var = "hello" %}

Sets a variable my_var. Variables can contain booleans, floats, strings, integers, and arrays. There is a special syntax, set_global, that can be used to set variables one level up in scope when working inside for loops (i.e. in the outer scope containing the loop, preserving its content between iterations).

if

Conditional statements are managed with if/is ... else ... endif

{% if my_var is ... %} ... {% endif %}

The list of is tests that are shipped with Zola are:

  • defined: the given variable is defined.
  • undefined: the given variable is undefined.
  • odd: the given variable is an odd number.
  • even: the given variable is an even number.
  • string: the given variable is a string.
  • number: the given variable is a number.
  • divisibleby: the given expression is divisible by the arg given.
  • iterable: Returns true if the given variable can be iterated over in Tera (i.e. is an array/tuple or an object).
  • object: Returns true if the given variable is an object (i.e. can be iterated over key, value).
  • starting_with(string): Returns true if the given variable is a string and starts with the arg given.
  • ending_with(string): Returns true if the given variable is a string and ends with the arg given.
  • containing(val): Returns true if the given variable contains the arg given.
    • strings: is the arg a substring?
    • arrays: is the arg one of the members of the array?
    • maps: is the arg a key of the map?
  • matching(regexp): Returns true if the given variable is a string and matches the regex in the argument.

for

Arrays are iterated with the for x in array/map ... endfor syntax.

{% for name in users %}
Hello: {{ name }}
{% endfor %}

Maps will provide a key/value pair. The names of the two fields are arbitrary:

{% for name, label in options %}
<option value="{name}">{label}</option>
{% endfor %}

Array filters (see below) are invoked before the loop. The following will print the list in reverse order.

{% for name in users | reverse %}{{ name }}{% endfor %}

include

Include other content into the current template being rendered. Include strings cannot be built out of variables, but they may be a list, in which case the first filename found is rendered. If the include block has the phrase 'ignore missing' at the end a missing file will not cause an error at build time.

{% include "header.html" %}

Macros

Macros are blocks of template that can be passed variables; they're basically just big chunks of content that can be parameterized. Macros must be defined in a file separate from the main content and imported with a distinct syntax from import:

{% import "header.html" as header %}

The header macro can be invoked in that file like this:

{{ header::header("My Blog!") }}

And an example of this macro (again, in the header.html file) would look like this:

{% macro header(title) %}
<header class="header">
  <h1>{ title }</h1>
</header>
{% endmacro %}


{% raw %}Content {{ goes here }}{% endraw %}

Content within the raw block will not be processed.