Build a Website with Zola

Build a Website with Zola' with Zola's logo

This is the tool I use currently for this website, and I like it as is pretty minimalist and fast.

You can check more about it in www.getzola.org, but still I will show you some cheats.

Installation

Depending on your Operative System, install Zola.

Linux

I prefer to install it via Flatpak:

flatpak install flathub org.getzola.zola

macOS

Available on Brew:

brew install zola

Windows

If for some reason you are still using Winshit Windows, is available in wget:

winget install getzola.zola

From now on, I will asume you are using Linux

CLI usage

If you installed via Flatpak, create an alias in your ~/.bashrc (bash) or ~/.zshrc (zsh):

alias zola="flatpak run org.getzola.zola"

Then reload your shell:

source ~/.zshrc

Now, go to your projects directory, like ~/Projects, and init a Zola project:

zola init my_website

Also you can navigate into an existing directory, and then just run:

zola init

You might be prompted by things like your website url, if you want to use SASS, and so on, you can change those settings in the generated zola.toml file in the root of your project.

Now you can run your project in local:

zola serve

Now open your browser, and navigate to http://127.0.0.1:1111, it should look something like this:

Initial looks of a *Zola* project

Initial project structure

Your project structure should look similar like this:

โ”œโ”€โ”€ content
โ”œโ”€โ”€ static
โ”œโ”€โ”€ templates
โ”œโ”€โ”€ themes
โ””โ”€โ”€ zola.toml

Configuration of zola.toml

Here is my base configuration, edit it as you like:

base_url = "https://juanmanzanero.com"
compile_sass = false
build_search_index = false
author = "Juan Manzanero"
output_dir = "build"
generate_feeds = true
default_language = "en"

I prefer to use pure CSS instead of SASS, change the output_dir (which is where the generated website files goes) to build, the default build directory is public/.

I recommend you to create a static/css/main.css.

Base template and home page

Create the base template

Create a new file: templates/base.html, this is the body of your HTML page:

<!doctype html>
<html lang="{{ lang }}">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
        <title>My website</title>
        <meta name="description" content="Hello world">
        <link rel="stylesheet" href="{{ get_url(path='css/main.css') }}">
    </head>
    <body>
        <main class="main-content">
            {% block content %}
            {% endblock %}
        </main>
    </body>
</html>

Create the home page

New file: templates/index.html:

{% extends "base.html" %}

{% block content %}
    <section class="hero">
        <h1>Hello world</h1>
        <p>This is my website built with Zola!</p>
        <a href="/blog">Blog posts</a>
    </section>
{% endblock %}

Adding content

Zola is focused on building content sites like this one, where you share blog posts or similar stuff.

To define your content, this goes in your content/ directory.

Start by creating content/_index.md:

+++
title = "My website"
description = "Hello world"
+++

# Hello world.

This is my website built with Zola!

Update your templates/base.html

Let us change the templates/base.html so it uses the title and description values from our content/_index.md:

<!doctype html>
<html lang="{{ lang }}">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />

        {% set lang_suffix = "" if lang == config.default_language else "." ~ lang %}
        {% set root_section = get_section(path="_index" ~ lang_suffix ~ ".md") %}

        {% if section is defined and section.ancestors | length > 0 %}
            {% set page_title = section.title ~ " | " ~ config.author %}
            {% set page_description = section.description %}
            {% set page_url = section.permalink %}
            {% set page_cover = section.extra.cover if section.extra.cover is defined else "" %}
        {% elif page is defined %}
            {% set page_title = page.title ~ " | " ~ config.author %}
            {% set page_description = page.description if page.description else root_section.description %}
            {% set page_url = current_url if current_url is defined else root_section.permalink %}
            {% set page_cover = page.extra.cover if page.extra.cover is defined else "" %}
        {% else %}
            {% set page_title = config.author %}
            {% set page_description = root_section.description %}
            {% set page_url = root_section.permalink %}
        {% endif %}

        <title>{{ page_title }}</title>
        <meta name="description" content="{{ page_description }}">
        <meta property="og:title" content="{{ page_title }}">
        <meta property="og:description" content="{{ page_description }}">
        {% if page_cover %}
            <meta property="og:image" content="{{ config.base_url }}/{{ page_cover }}">
        {% endif %}
        <meta property="og:url" content="{{ page_url }}">
        <meta property="og:type" content="website">

        <link rel="stylesheet" href="{{ get_url(path='css/main.css') }}">
        <link rel="icon" type="image/x-icon" href="{{ get_url(path='favicon.ico') }}" />
    </head>
    <body>
        <main class="main-content">
            {% block content %}
            {% endblock %}
        </main>
    </body>
</html>

If you check your website, it should look like this:

Our project with the base.html, index.html, and content/_index.md

Do you see the title in the tab bar?

Check your web tools and look for the description tag, the description you typed in content/_index.md will be there.

Adding subpages: /blog

Create content/blog directory, then create a file: content/blog/_index.md:

+++
title = "Blog"
description = "These are my posts"
sort_by = "date"
template = "blog.html"
+++

Create content/blog/hello-world directory, then inside create content/blog/hello-world/index.md file:

+++
title = "Hello world"
description = "My first post!"
date = 2026-09-01
+++

And I did it with [Zola](https://www.getzola.org)

Create templates/blog.html, and add the following:

{% extends "base.html" %}

{% block content %}
    <section class="blog-section">
        <h1>{{ section.title }}</h1>
        {% if section.description %}
            <p>{{ section.description }}</p>
        {% endif %}

        <ul class="posts-list">
            {% for post in section.pages %}
                <li class="post-item">
                    <a href="{{ post.path }}">
                        <h3>{{ post.title }}</h3>
                    </a>
                    <time datetime="{{ post.date }}">
                        {{ post.date | date(format="%d %B %Y") }}
                    </time>
                </li>
            {% endfor %}
        </ul>
    </section>
{% endblock %}

Once again, we extend our base.html.

Then we iterate through the section.pages property, as we defined template = "blog.html" in content/_index.md, Zola retrieves that file for blog.html.

It should look like this:

Blog page rendered

Now add a new template: templates/page.html:

{% extends "base.html" %}

{% block content %}
<article class="post">
    <h1>{{ page.title }}</h1>
    {% if page.date %}
        <time datetime="{{ page.date }}">
            {{ page.date | date(format="%B %d, %Y") }}
        </time>
    {% endif %}
    <p>{{ page.description }}</p>
    {{ page.content | safe }}
</article>
{% endblock %}

It looks like this:

Post page rendered

Keep adding posts

Now try adding new posts just like blog/hello-world/index.md, each of them will be created by Zola.

Internationalization (i18n)

Content in other languages

This website serves content in both English and Spanish, you can configure Zola for supporting multiple languages.

default_language = "en"

[languages.en]

[languages.es]

This will show content in English if we are in the root page /, and Spanish content if we are in /es.

Still if you visit /es, you will see your orginal content, let us fix that.

Create content/_index.es.md:

+++
title = "Mi website"
description = "Hola mundo"
+++

# Hola mundo

Este es mi website constrido con Zola!

Create content/blog/_index.es.md, similar to the default one:

+++
title = "Blog"
description = "Estos son mis posts."
sort_by = "date"
template = "blog.html"
+++

Now create a new file, content/blog/hello-world/_index.es.md:

+++
title = "Hola mundo"
description = "ยกMi primer post!"
date = 2026-09-01
slug = "hola-mundo"
+++

Y lo hice con [Zola](https://www.getzola.org).

If you navigate to /es, it will look like this:

Home page in Spanish Blog page in Spanish Hello world in Spanish

Translations

We can also add translations if we need to render text directly in our templates.

Add this to your zola.toml:

[translations]
hello = "hello"

[languages.en]

[languages.es]
[languages.es.translations]
hello = "hola"

As English is our default language, we just add the keys to [translations], not in [languages.en.translations].

To use a translations, use the trans(key) function in an HTML template:

<p>{{ trans(key='hello') }}</p>

Conclusion

Now you can add styles in your static/css/main.css, modify the pages structure, and so on.

In a future post, I will share some advanced techniques and tricks for making it easier work with Zola.