Who is Hugo? Or in this context, what is Hugo?

It is a software to create websites. It takes some input (markdown files) and generates a website from it (html, css and other files). This type of website software is called a static site generator. It is static, because the built website is literally just some files that are being sent to the visitor. Many modern websites generate websites on-the-fly when you visit them, based on your user, your language, your location. This makes them usually slower and also more complex. A static site generator is quite simple and fast and in the case of Hugo, very adaptable customizable!

You should try it out! Visit the Hugo homepage and make sure to check out some of the themes.


This website is the first time of me using Hugo. So I am learning on the go. As a living documentation, I am growing this page of all I understand of Hugo.



Hugo Glossary
Hugo Neso Theme

Basic Concepts

Main folders:
content/   ← what you say
layouts/   ← how it looks
static/    ← files copied with no changes

Hugo automatically assigns a kind to each page.

- home → the front page, nothing else
- page → single content file, static page or blog post
- section → folder like `posts/` with multiple pages inside
- taxonomy → tags, categories, etc

Example:

content/posts/hello.md

This is section: posts, kind: page

content/posts/_index.md

This is section: posts, kind: section (list page)

Terms, Taxonomies and Sections

Section
A section is a landing page for all pages inside a folger. E.g. all files in posts are a section. The layout section.html is there to show all files inside one directory, or in Hugo speak, one section. For this, the _index.md is used. Typical use case would be an index of all blog entries.

Lets take this example directory structure:

/content/posts
    _index.md
    /01-hello-world
        index.md
    /02-some-thoughts
        index.md
    03-a-project.md

Now visiting example.com/posts/ would use the section.html layout fill it with content using content/posts/_index.md and all folders and files inside the posts directory.

The name of the section can be changed or defined in the _index.md in the frontmatter using the title = 'Section Name' property. This will also be the one used in the term overview later.

Taxonomy and Terms
A taxonomy is a way to categorize things. A sorting system basically. This can be anything, e.g. a category, tag or topic. The values of one of those taxonomies are its terms. For example, I might have the taxonomy tags and its values are tech, art and music. This would leave me with the the following urls:

/tags/          <-- Taxonomy page made from taxonomy.html layout. Shows all terms of the taxonomy.
/tags/tech      <-- Term page made from term.html layout. Shows all pages with this term.
/tags/art       <-- Term page made from term.html layout. Shows all pages with this term.
/tags/music     <-- Term page made from term.html layout. Shows all pages with this term.

To fill a e.g. a term page with custom content, create the following file /content/<taxonomy-name>/<term-name>/_index.md and fill it e.g. with:

---
title: 'tech'
---

These are all the things related to technology.

Same applies for a taxonomy.

Leaf vs Branch bundles

leaf bundle

A leaf bundle is a directory that contains an index.md file and zero or more resources. Analogous to a physical leaf, a leaf bundle is at the end of a branch. It has no descendants.

branch bundle

A branch bundle is a directory that contains an _index.md file and zero or more resources. Analogous to a physical branch, a branch bundle may have descendants including leaf bundles and other branch bundles. Top-level directories with or without _index.md files are also branch bundles. This includes the home page.

Images in page bundle, assets or static folder

Images can live in three directories: page-bundle, assets and static.

/content/
    /page-bundle
        /index.md
        /picture01.png
/assets/
    /img/
        /picture02.png
/static/
    /img/
        /picture03.png

The page bundle makes the most sense, when the image is only used in this page. Then it is called with {{ with .Page.Resources.Get $path }}.

The static directory only makes sense for pictures that are final, should not be touched by Hugo and need stable links. This is great for logos, favicons, etc.

The assets folder is for global resources that can still be accessed by the Hugo pipelines, e.g. resizing or fit. This can be called with {{ with resources.Get $path }}.

Context object

Every template gets a context object: . (dot)

Common fields:

  • .Title
  • .Content
  • .Date
  • .Params
  • .Kind
  • .Section

In a loop, the context . switches to the page

<section>
  <h2>
    <a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
  </h2>
</section>

Inside range:

  • . is no longer the homepage

  • . is each page in turn

  • .Content is already HTML

  • never wrap it in <p> or escape it

Creating new pages via Hugo CLI

# As single files
hugo new <section name>/'<post title>'

# As leaf bundles
hugo new <section name>/'<post title>'/index.md

Variables

They:

  • start with $
  • exist only inside the current template / block
  • are immutable (you don’t “change” them later)

Templates and Partials

A partial is a function that returns HTML. It takes content, usually from markdown files and builds HTML with it.

Other

Dates in Hugo

2026-01-01T17:00:00+01:00
YYYY-MM-DDTHH:MM:SS+HH:MM (Time zone difference)
// The T delimits the date from the time
  1. Rightclick on heading, inspect and copy ID
  2. Use this <a href="/section/postname#id">

Setup new site

When installing a new theme and asked to enter hugo mod init github.com/USER/REPO, any link-like string can be used (excluding https://), but it makes sense to use the link to the repo.

Creating a theme

  • home.html and index.html is the same
  • /layouts/ containts templates
  • /layouts/partials/ contains partial templates

Shortcodes

A shortcode is a template invoked within markup, accepting any number of arguments. There are some embedded shortcodes, but you can also create custom ones.

Show Hugo shortcodes in code blocks

If you come to the point that you want to show others how to use a shortcode inside a code block, Hugo still convertes them, even inside code block. To prevent this, a comment indicator needs to be added to them. So it will be {{</*.

Create a custom shortcode

  1. Create a file in layouts/shortcodes/name.html.
  2. Create the shortcode, e.g. like
    {{ with resources.Get (.Get "src") }}
        <audio controls preload="auto" src="{{ .RelPermalink }}"></audio>
    {{ end }}
    
  3. Call the shortcode within the file with {{</* audio src=/audio/test.mp3 >}}

Delegate a shortcode to a partial

If you want something to be executable as a partial and as a shortcode, create a partial as the base logic. E.g. here is the tag partial from my theme:

{{/* Small tag, like an inline callout */}}

{{- $text := .text -}}
{{- $style := .style -}}

{{- if $text -}}
<span class="tag tag--{{ $style }} tag--{{ $text | urlize }}">
  {{ $text }}
</span>
{{- end -}}

Then, create a shortcode that delegates to the partial, or calles it. E.g.:

{{/* Delegate content to the partial */}}

{{ partial "tag.html" (dict
  "text" (.Get "text" | default (.Get 0))
  "style" (.Get "style")
) }}

This way it can be used both as an partial with {{ partial "tag.html" (dict "text" "pending ⏳" "style" "note") }} or as a shortcode with {{< tag text="pending ⏳" style="note" >}}.

Guides

Selfhost and serve fonts with a Hugo theme

As I somehow have strong opinions about aestetics, being able to select the right font was important. Here are the steps I took:

  1. Download the files

    1. Go to google-webfonts-helper and search for the fonts you like
    2. Select all the variants you want, download the fonts at the bottom and place them in the Hugo project under /static/fonts/font-family-name/.
  2. Load the fonts with CSS

    1. This will be done with a layout partial inside the theme. Create a file theme/layouts/_partials/fonts.html.
    2. Go back to the website, enter /fonts/font-family-name/ as a folder prefix and copy the generated css code.
    3. Paste the css into the fonts.html partial like so and make sure to check if the paths are correct:
      <!-- Template that loads the fonts which 
      are exposed in the /static/fonts/ directory -->
      
      <style>
      /* IBM Plex Mono */
      
      /* ibm-plex-mono-regular - latin */
      @font-face {
      font-display: swap;
      font-family: 'ibm_plex_mono';
      font-style: normal;
      font-weight: 400;
      src: url('/fonts/ibm_plex_mono/ibm-plex-mono-v20-latin-regular.woff2') format('woff2'); 
      }
      
      /* ibm-plex-mono-italic - latin */
      @font-face {
      font-display: swap;
      font-family: 'ibm_plex_mono';
      font-style: italic;
      font-weight: 400;
      src: url('/fonts/ibm_plex_mono/ibm-plex-mono-v20-latin-italic.woff2') format('woff2');
      }
      
      /* ... more font variants */
      </style>
      
    4. Now the partial needs to be included inside the hugo page. For this, edit baseof.html and include the partial inside the header, like so:
      <!DOCTYPE html>
      <html lang="{{ site.Language.LanguageCode }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
      <head>
          {{ partial "head.html" . }}
          {{ partial "fonts.html" . }} <!-- here it is loaded -->
      </head>
          <body>
              <div class="site-wrapper">
                  <header>
                      {{ partial "header.html" . }}
                  </header>
                  <main>
                      {{ block "main" . }}{{ end }}
                  </main>
                  <footer>
                      {{ partial "footer.html" . }}
                  </footer>
              </div>
          </body>
      </html>
      
  3. Select the font for the site

    1. The font is loaded in css and available to be used, but currently it is not used yet. We need to say we want to use it. To make it nice and configurable, we will make use of the hugo.toml to decide which fonts are being used. For this, edit /hugo.toml and add:
      [params.fonts]
      body = "ibm_plex_mono" # This must match the `font-family` in `fonts.html`
      head = "ibm_plex_sans"
      code = "ibm_plex_mono"
      
    2. Now we read the selected fonts from the hugo.toml and set them as css variables to be used lates. Add the following at the bottom of fonts.html, which we created earlier:
      </style>
      
      /* ... font face load */
      
      /* Load configuration and save as variables */
      :root {
      --font-body: "{{ .Site.Params.fonts.body }}";
      --font-code: "{{ .Site.Params.fonts.code }}";
      --font-head: "{{ .Site.Params.fonts.head }}";
      }
      
      </style>
      
    3. And lastly, we select these fonts to be used inside the css. For this, go to your css, in my case theme/assets/css/main.css and load the fonts like so:
      body {
          /* ... */
          font-family: var(--font-body), system-ui, sans-serif;
          /* ... */
      }
      
      pre,
      code {
          font-family: var(--font-code), monospace;
          /* ... */
      }
      
      h1,
      h2,
      h3 {
          /* ... */
          font-family: var(--font-head), system-ui, sans-serif;
      }
      
  4. Add new fonts later. To add new fonts later, you just need to

    1. Download and put them in `/static/fonts/font-family/
    2. Load them in /theme/layouts/_partials/fonts.html
    3. Select it in hugo.toml

/hugo/

topics: tech, web