Zola is a static site generator (SSG) written in Rust. It relies on Terra template engine. That’s all for the technical nerd speak, let’s get to building.
Start by initialing the project. Run:
zola init myblog
There will be several prompts. Provide a website address (can be a dummy). Also, feel free to reply N
to all of them, we will configure everything afterwards.
Structure
By default, the generator expects this project structure:
├── config.toml
├── content/
│ └── blog/
│ ├── _index.md
│ ├── first.md
│ └── second.md
├── sass/
├── static/
├── templates/
│ ├── base.html
│ ├── page.html
│ ├── list.html
│ └── index.html
└── themes/
Here’s how it works:
-
config.toml
— is responsible for the website configuration. Title, author, RSS, and everything; -
base.html
— functions as a main template for the website aka layout; -
index.html
— is exactly what you think it is; -
list.html
— is the template, responsible for displaying lists (of posts, for example); -
page.html
— the template that diplays contents of a post. Namely, it is where.md
content is rendered;
Templates
Create the templates/
folder.
Let’s begin with the templates/base.html
:
<!DOCTYPE html>
<html lang="en">
<head>
{% include "head.html" %}
</head>
<body>
<main>
{% block content %} {% endblock %}
</main>
{% include "footer.html" %}
</body>
</html>
Notice there are placeholders for head.html
, navigation.html
, and footer.html
. Let’s create those as well in the templates/
.
# head.html
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<meta name="robots" content="index, follow">
# footer
<footer>
My super cool blog 2024
</footer>
Notice: in navigation.html
there are some items coming from config.extra
. We are going to create them next.
# navigation
<nav>
{% for item in config.extra.navigation %}
<a
href="{{ item.url }}">{{ item.name }}</a>
{% endfor %}
</nav>
Lastly, let’s create templates/general.html
for pages like index, contact, etc:
{% extends "base.html" %}
{% block content %}
<h1>
{% if section.title -%}
{{ section.title }}
{%- endif %}
{% if page.title -%}
{{ page.title }}
{%- endif %}
</h1>
<p>
{% if section.description -%}
{{ section.description }}
{%- endif %}
{% if page.description -%}
{{ page.description }}
{%- endif %}
</p>
<article>
{% if section.content -%}
{{ section.content | safe }}
{%- endif %}
{% if page.content -%}
{{ page.content | safe }}
{%- endif %}
</article>
{% endblock content %}
Configuration
In Zola, config.toml
is the source of truth. There, we put the project’s title, description, RSS, navigation (if you prefer), and everything else. Don’t forget to check out Zola Docs for more. Here’s a sensible starter:
base_url = "https://my-cool-blog.com"
title = "Your blog's title"
description = "The description goes here"
default_language = "en"
minify_html = true
generate_feed = true
feed_filename = "atom.xml"
author = 'Arya Stark'
compile_sass = false
build_search_index = false
[markdown]
render_emoji = true
external_links_target_blank = true
external_links_no_follow = true
external_links_no_referrer = true
smart_punctuation = true
lazy_async_image = true
[link_checker]
internal_level = "error"
external_level = "error"
[slugify]
paths = "on"
anchors = "on"
[extra]
navigation = [
{ url = "/now", name = "now"},
{ url = "/journal", name = "journal"},
{ url = "/workshop", name = "workshop"},
{ url = "/bookmarks", name = "bookmarks"},
]
Content
The conten will live in a dedicated folder. Let’s create it: /content
. Here we are going to have subfolders. Think of them as types of content you post: blog posts, videos, or podcast episodes. We can hav as many subfolders as we want. For example, I settled for journal, workshop and bookmarks for my personal website.
Such sections can include _index.md
, which defines the content and metadata for the section. Let’s create content/blog/_index.md
.
+++
title = "I am a section title"
description = "I am a section description"
template = "list.html"
paginate_by = 8
sort_by = "date"
+++
Mind the dividers +++
.
It all starts with frontmatter. By default, _index.md
is for listing content. But you can write some markdown as well. It will be rendered on the section page consequently.
Les’t create a couple of articles. Now, in content/blog/
create article1.md
:
+++
title = "Article title"
description = "Some fancy description can go here as well"
date = "2024-01-05"
draft = false
template = "page.html"
+++
# Heading 1
Ut culpa quis et ut nulla occaecat elit ad commodo consequat. Ea nostrud ut adipisicing et ipsum nostrud cillum non magna in ad. Est reprehenderit exercitation excepteur ut laborum minim non occaecat amet.
## Heading 2
Laborum aliqua duis commodo irure anim occaecat Lorem deserunt. Dolore dolor adipisicing ad sint non dolor ut Lorem consectetur fugiat quis. In aute non in amet velit ut dolor adipisicing magna reprehenderit incididunt labore incididunt.
### Heading 3
Esse proident ullamco id adipisicing velit ad do sint nisi commodo consectetur. Commodo enim consequat eu dolore aliqua pariatur consectetur eiusmod quis minim ut irure. Non aliquip laboris occaecat sunt adipisicing nisi occaecat occaecat esse Lorem ullamco.
- Bullet 1
- Bullet 2
- Bullet 3
1. Argument 1
2. Argument 2
3. Argument 3
>Some super smart quote
Render content
How to render it? Hint: notice the template
field in both frontmatters above. Let’s create them, starting with templates/list.html
:
{% extends "base.html" %}
{% block content %}
<h1>{{ section.title }}</h1>
<!-- content list -->
<ul>
{% for page in paginator.pages %}
<li>
<a href="{{ page.permalink | safe }}">
{{ page.title }}
</a>
</li>
{% endfor %}
</ul>
<!-- paginator -->
{% if paginator %}
{% if paginator.previous %}
<a href="{{ paginator.previous }}">← Previous</a>
{% endif %}
{% if paginator.next %}
<a href="{{ paginator.next }}">Next →</a>
{% endif %}
{% endif %}
{% endblock content %}
Next, let’s create templates/page.html
:
{% extends "base.html" %}
{% block content %}
<h1>{{ page.title }}</h1>
<article>{{ page.content | safe }}</article>
<!-- paginator -->
{% if paginator %}
{% if paginator.lower %}
<a href="{{ page.lower.permalink | safe }}">← Previous</a>
{% endif %}
{% if paginator.higher %}
<a href="{{ page.higher.permalink | safe }}">Next →</a>
{% endif %}
{% endif %}
{% endblock content %}
Style
There are many tools that allow creating great-looking interfaces. If you prefer working with plain css, create static/style.css
and work here. Or, if you like working with Tailwind, the simplest way is to update your head.html
:
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio,line-clamp"></script>
Conclusion
Zola is a great site generator: easy to use, it has great markdown support, zero dependencies, and clean developer experience. It is also scalable, extensible and comes with a dedicated catalog of themes.
Originally published here.