Apr 24, 2026 9.1 KB markdown

how to create a zola site

a walkthrough for building a static site with zola on sublimated. zola is a fast static site generator that uses markdown content and tera templates.

live demo: docs.sublimated.com/zola/

folder structure

minimum zola site:

mysite/
  config.toml         site configuration (required)
  content/
    _index.md         homepage
  templates/
    base.html         shared layout
    index.html        homepage template
    page.html         single page template
    section.html      section listing template

1. config.toml

the only required field is base_url. sublimated uses this to detect zola sites.

base_url = "https://myspace.sublimated.com/mysite"
title = "my site"
description = "a zola site on sublimated"
compile_sass = false
build_search_index = false
generate_feeds = false


[extra]

rules for base_url:

  • custom domain: https://mysite.com

  • subdomain: https://myspace.sublimated.com/mysite

  • must match the URL where the site will be served

2. content

content files are markdown with TOML frontmatter (between +++ markers).

+++
title = "my first post"
date = 2026-04-20
description = "hello world"
[taxonomies]
tags = ["demo", "first"]
+++


the post body goes here. standard markdown: **bold**, *italic*, [links](https://example.com), `code`.

content organization

  • content/_index.md — homepage (or section root)

  • content/blog/_index.md — creates a /blog/ section with its own listing

  • content/blog/my-post.md — a page at /blog/my-post/

  • content/about.md — standalone page at /about/

sections vs pages

  • a folder with _index.md is a section — it lists its child pages

  • a standalone .md file is a page

  • sections use section.html template, pages use page.html

co-located assets (images)

put images next to your markdown by using a folder with index.md:

content/blog/my-post/
  index.md        the post content
  photo.jpg       referenced as ![](photo.jpg)
  diagram.png

sublimated stores binary files in CAS and rewrites URLs to serve them from CDN. no special config needed.

frontmatter fields

+++
title = "page title"                    # required
date = 2026-04-20                       # for sorting
description = "page description"        # meta description
draft = false                           # hide from listings
weight = 1                              # manual sort order


[taxonomies]
tags = ["tag1", "tag2"]                 # tag pages auto-generated


[extra]
author = "name"                         # custom fields, access via page.extra.author
image = "photo.jpg"                     # custom fields for templates
+++

summary separator

use <!-- more --> to split a post into summary and body:

this part shows in listings as the summary.


<!-- more -->


this part only shows on the full page.

access in templates: {{ page.summary }} and {{ page.content }}.

3. templates

templates use tera syntax with inheritance.

base.html — shared layout

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>{% block title %}{{ config.title }}{% endblock %}</title>
  <style>
    /* all CSS goes here — inline for best performance */
    body { font-family: -apple-system, sans-serif; max-width: 700px; margin: 0 auto; padding: 1rem; }
    a { color: #1a5cc8; }
  </style>
</head>
<body>
  <header><a href="/">{{ config.title }}</a></header>
  <main>{% block content %}{% endblock %}</main>
  <footer>powered by zola + sublimated</footer>
</body>
</html>

index.html — homepage

{% extends "base.html" %}
{% block content %}
  {{ section.content | safe }}
  {% set posts = get_section(path="blog/_index.md") %}
  {% for page in posts.pages %}
    <article>
      <a href="{{ page.permalink }}">{{ page.title }}</a>
      <time>{{ page.date | date(format="%B %d, %Y") }}</time>
    </article>
  {% endfor %}
{% endblock %}

page.html — single page

{% extends "base.html" %}
{% block title %}{{ page.title }} | {{ config.title }}{% endblock %}
{% block content %}
  <h1>{{ page.title }}</h1>
  <time>{{ page.date | date(format="%B %d, %Y") }}</time>
  <span>{{ page.reading_time }} min read</span>
  {{ page.content | safe }}
{% endblock %}

section.html — section listing

{% extends "base.html" %}
{% block content %}
  <h1>{{ section.title }}</h1>
  {{ section.content | safe }}
  {% for page in section.pages %}
    <article>
      <a href="{{ page.permalink }}">{{ page.title }}</a>
      <time>{{ page.date | date(format="%B %d, %Y") }}</time>
    </article>
  {% endfor %}
{% endblock %}

useful template variables

  • config.title, config.description, config.base_url

  • page.title, page.date, page.content, page.permalink

  • page.reading_time, page.word_count, page.toc

  • page.taxonomies.tags — array of tag names

  • page.extra.* — custom frontmatter fields

  • page.summary — content before <!-- more -->

  • section.pages — child pages of a section

  • current_path — current URL path (useful for nav active states)

4. shortcodes

shortcodes are reusable snippets. create tera templates in templates/shortcodes/.

templates/shortcodes/note.html:

<div style="background:#dbeafe;border-left:3px solid #3b82f6;padding:0.75rem 1rem;margin:1rem 0;border-radius:4px">
  <strong>Note:</strong> {{ body }}
</div>

use in markdown:

{%/* note() */%}
this is an important note.
{%/* end */%}

5. taxonomies (tags)

enable in config.toml:

taxonomies = [{ name = "tags" }]

create templates:

  • templates/tags/list.html — all tags page

  • templates/tags/single.html — posts for one tag

add tags in frontmatter:

[taxonomies]
tags = ["cooking", "travel"]

zola auto-generates /tags/ and /tags/cooking/ pages.

6. push and build

via git:

git add .
git commit -m "my zola site"
git push

or via CloudStorage — just save files, they sync automatically.

sublimated detects config.toml with base_url, sends text files to the zola WASM builder, stores the output, and serves it at your URL. builds typically take under 1 second.

7. image resizing

append query params to image URLs for on-demand resizing:

<img src="{{ page.extra.image }}?w=128">
  • ?w=128 — resize to 128px wide (keeps aspect ratio)

  • works on any image served through sublimated CDN

zola can generate a client-side search index using elasticlunr.js.

enable in config.toml:

build_search_index = true

this produces two files in the build output:

  • elasticlunr.min.js — the search library

  • search_index.en.js — the index data (sets window.searchIndex)

create a search page:

content/search.md:

+++
title = "search"
template = "search.html"
+++

templates/search.html:

{% extends "base.html" %}
{% block content %}
<h1>search</h1>
<input type="text" id="search-input" placeholder="type to search..." autofocus />
<p id="search-meta"></p>
<ul id="search-results"></ul>


<script src="{{ config.base_url }}elasticlunr.min.js"></script>
<script src="{{ config.base_url }}search_index.en.js"></script>
<script>
(function () {
  var index = elasticlunr.Index.load(window.searchIndex);
  var input = document.getElementById("search-input");
  var results = document.getElementById("search-results");
  var meta = document.getElementById("search-meta");


  input.addEventListener("input", function () {
    var query = input.value.trim();
    results.innerHTML = "";
    if (!query) { meta.textContent = ""; return; }
    var hits = index.search(query, { expand: true });
    meta.textContent = hits.length + " result" + (hits.length !== 1 ? "s" : "");
    for (var i = 0; i < hits.length; i++) {
      var doc = index.documentStore.getDoc(hits[i].ref);
      var li = document.createElement("li");
      var body = (doc.body || "").substring(0, 200);
      li.innerHTML = '<a href="' + doc.id + '">' + (doc.title || doc.id) + '</a>'
        + '<p>' + body + (body.length >= 200 ? '...' : '') + '</p>';
      results.appendChild(li);
    }
  });
})();
</script>
{% endblock %}

live example: docs.sublimated.com/zola/search/

what works

  • markdown with TOML frontmatter

  • tera templates with inheritance

  • syntax highlighting (40+ themes, 200+ languages)

  • shortcodes (body and inline)

  • taxonomies (tags, categories)

  • table of contents (auto-generated from headings)

  • internal links (@/path/to/file.md — validated at build time)

  • reading time and word count

  • Atom/RSS feeds

  • clean URLs

  • dark mode (CSS prefers-color-scheme)

  • co-located images and binary assets

  • client-side search (elasticlunr.js)

what doesn't work yet

  • sass/scss compilation

  • zola image processing (resize_image())

  • load_data() from external URLs

  • multilingual sites (default_language config)

real example

the kanapka site is a live reference: kanapka.sublimated.com — a food blog with sections, author pages, image thumbnails, and responsive layout.