+++ 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](https://getzola.org) is a static site generator written in [Rust](../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](https://tera.netlify.app). ## Running Zola - `zola init` - Starts a new Zola project. Not for the faint of heart. - `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](https://toml.io/en/) 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](../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](https://serde.rs/)). 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`: ```jinja2 {% 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` ```jinja2 {% 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. ```jinja2 {% for name in users %} Hello: {{ name }} {% endfor %} ``` Maps will provide a key/value pair. The names of the two fields are arbitrary: ```jinja2 {% for name, label in options %} {% endfor %} ``` Array filters (see below) are invoked before the loop. The following will print the list in reverse order. ```jinja2 {% 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. ```jinja2 {% 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`: ```jinja2 {% import "header.html" as header %} ``` The header macro can be invoked in that file like this: ```jinja2 {{ header::header("My Blog!") }} ``` And an example of this macro (again, in the `header.html` file) would look like this: ```jinja2 {% macro header(title) %}

{ title }

{% endmacro %} ``` ```jinja2 {% raw %}Content {{ goes here }}{% endraw %} ``` Content within the `raw` block will not be processed.