HugoPress reference site

Building a headless CMS on Hugo

Why we split authoring from delivery, and how HugoPress wires a rich editor to a static site through Git.

Tomáš Skočdopole Tomáš Skočdopole 2 min read
Building a headless CMS on Hugo

A classic monolithic CMS bundles authoring, storage and rendering into one running process. That is convenient — until you start caring about performance, security and scaling. The headless approach decouples those three responsibilities, and that decoupling is exactly what HugoPress is built on.

Three layers, three responsibilities

  1. The editor — authors write in a block editor (Editor.js). The output is clean structured JSON, not WYSIWYG HTML soup.
  2. The domain layer — the CMS turns blocks into finished HTML via the BlockRenderingPipeline. The theme itself knows nothing about Editor.js.
  3. Delivery — the rendered content is written as content/<lang>/... and committed to Git. Hugo builds a static site from it.

The theme does not know Editor.js. All renderers live on the CMS side, which keeps the template dumb, portable and easy to test.

Why Git is the transport layer

Git gives us three things almost for free:

  • History — every publish is a commit, so we get audit and rollback.
  • Review — preview runs on the same repository as production.
  • Idempotency — if the content has not changed, there is nothing to commit, and the publish is skipped.
public function publish(Article $article): void
{
    $html = $this->pipeline->render($article->blocks());
    $this->repository->writeContent($article->locale(), $article->slug(), $html);
    $this->git->commitIfChanged(sprintf('publish: %s', $article->slug()));
}

What you get

A static site has no runtime to attack, no database queries on the request path, and it can be served from a CDN as close to the reader as possible. Meanwhile the author works in a comfortable editor and never thinks about the build.

In the next article we look at why a site like this is so fast.

Tomáš Skočdopole

By

Tomáš Skočdopole

Senior engineer, INTEVIA

Builds headless publishing pipelines and writes about turning content into fast, durable static sites.

Related articles

Performance CDN Hugo

Why static sites win on the edge

When a user opens a dynamic page, the server typically reaches into a database, assembles HTML, and only then replies. On a static site that work is already done at build time — all that is left for the request is to ship a finished file. What you save The database round-trip — gone; the content is already in the HTML. Runtime time — no PHP/Node process on the critical path. Distance — the file sits in a CDN POP a few dozen kilometres from the reader. Measurable impact Metric Dynamic Static on CDN TTFB ~280 ms ~25 ms LCP (median) 2.4 s 0.9 s Cost per request non-trivial ~zero The fastest database query is the one that never has to run.

Read more
i18n Content Hugo

Multilingual the painless way

Multilingual support is a source of pain in most systems — duplicated templates, drifting translations, broken links between languages. Hugo handles it surprisingly elegantly, and HugoPress only smooths a few rough edges. One layout, two languages The section data lives in each page’s front matter; the layout is shared. The same layouts/blog/list.html template reads .Params.* and renders both the cs and en variant — no duplicated logic. Pairing via translationKey Give both versions the same translationKey:

Read more