Hugo: don't miss the best part

Hugo is a fantastic piece of software, IMHO. It's highly flexible and covers lots of great features, which saves hours of work. If you want to dive into theme development, this could be a great contribution for the community. However, it's easy to miss critical concepts if you don't spend enough time learning how to do it right. Learn the template hierarchy Hugo is a framework, so it has its specificity. It's impossible to cover everything here, but, roughly speaking, many templates will inherit from generic structures, so you can prevent unnecessary duplication (e.g., HTML markup) just by defining blocks and using hooks: layouts/ ├── _default/ │ ├── _markup/ │ │ ├── render-image.html

May 3, 2025 - 16:04
 0
Hugo: don't miss the best part

Hugo is a fantastic piece of software, IMHO.

It's highly flexible and covers lots of great features, which saves hours of work.

If you want to dive into theme development, this could be a great contribution for the community.

However, it's easy to miss critical concepts if you don't spend enough time learning how to do it right.

Learn the template hierarchy

Hugo is a framework, so it has its specificity.

It's impossible to cover everything here, but, roughly speaking, many templates will inherit from generic structures, so you can prevent unnecessary duplication (e.g., HTML markup) just by defining blocks and using hooks:

layouts/
├── _default/
│   ├── _markup/
│   │   ├── render-image.html   <-- render hook
│   │   └── render-link.html    <-- render hook
│   ├── baseof.html
│   ├── home.html
│   ├── section.html
│   ├── single.html
│   ├── taxonomy.html
│   └── term.html
├── articles/
│   └── card.html               <-- content view
├── partials/
│   ├── footer.html
│   └── header.html
└── shortcodes/
    ├── audio.html
    └── video.html

source: template types

Again, this is very specific to Hugo here, but I want to highlight the baseof.html template:

DOCTYPE html>
<html lang="{{ or site.Language.LanguageCode }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
<head>
  {{ partial "head.html" . }}
head>
<body>
  <header>
    {{ partial "header.html" . }}
  header>
  <main>
    {{ block "main" . }}{{ end }}
  main>
  <footer>
    {{ partial "footer.html" . }}
  footer>
body>
html>

At its core, it may look similar to other template engines, but some themes developers could miss the point.

The wrapping system

Recently, I've found a free theme that illustrate the issue.

I found several files that contain a line, which seems weird.

I looked into it, and there was no good reason for such duplication.

The author duplicated the entire HTML markup for each view (home, page, single article, etc).

While it's possible, it's a bad practice.

The following lines in the baseof.html template are meant to prevent unnecessary repetition:

  <main>
    {{ block "main" . }}{{ end }}
  main>

Then, in your template (e.g., single.html) you only have to define the main block to inject your code, for example:

{{- define "main" }}
  <article>
    <header class="post-header">
     <h1>{{ .Title }}h1>
   header>
   <div class="post-content">
    {{ .Content }}
   div>
   <footer class="post-footer">
    {{ partial "article-pagination.html" . }}
   footer>
  article>
{{- end }}

Is it that bad?

Not really.

Besides, the author was kind enough to share the code of his personal blog, for free.

However, duplication is problematic for maintenance, especially when your theme heavily rely on CSS classes and nested div tags.

Wrap up

Leverage Hugo blocks and templates types.

This way, you only have to modify/add the HTML markup that is specific your view.