Hugo templating basics

Go templates is the flexible templating language used in Hugo. In this lesson we'll go over basic Go templating concepts and see how you can use it on your site.

What you’ll learn here:

  • The basics of the powerful Go templating language:

  • Syntax

  • Variables

  • Context - “.”

  • Loops

  • Logic and comparisons

  • Dictionaries

  • Useful built-in functions

# Starting repo
git clone

# Starting branch:
git checkout templating-intro-start 

# Finished branch:
git checkout templating-intro-finish 

You probably want to get stuck in and create an actual site now. Feel free to skip to the next lesson for that, and refer to this later. But learning the Go templating language is one of the most important things in learning Hugo, and it has a few confusing things to learn.

What is the Go templating language?

Go templating is how you take control of your static content: it’s a powerful way to make your site more manageable, by reducing duplication in your layouts and accessing data inside your front matter - or even elsewhere in your site!

Templating lets you write code inside of HTML. You can create variables, “query” data, loop over data, and transform content, saving you a lot of time. If you’re used to Jekyll and Liquid, Go templating can be a bit confusing, but it is also a much more powerful templating language.

We don’t want to go too much into detail, so we’ll go over the useful basics here. Hugo documentation is great, and there’s far too much to cover in one page.


If you’re used to Liquid templating from Jekyll, you’ll be happy to know that, although Go templating can be a bit harder to work with, normal code blocks are simpler:

  • Code segments must be surrounded by double curly braces: {{ }}.
  • Blocks of code need to indicate where they end: {{ end }}.

As you can see, there’s no {% %} or {% endfor %}, {% endif %}, etc. The syntax is slightly different for shortcodes, which is a later topic.


One thing that can be a bit confusing is the “dot” symbol you see everywhere in Hugo (for example, block "main" .). This refers to the current “context” or “scope” in a page, loops, etc. This will become a bit clearer with examples.

To refer to things outside of the local “.” scope, add the “$” symbol in front, or save values into a named variable that you can use elsewhere.


Be careful with HTML comments in your code - Go templating will ignore HTML comments surrounding code, potentially causing you trouble:

<!-- {{ $noError := "Everything's going fine...right?" }} {{ $noError }} -->

If you want to comment out an area, so that Hugo ignores it, use this syntax:

{{/* {{ $noError }} */}}

Practical work: Go templating in action

To follow along and gain some hands-on practice, we’ve created a checkpoint in our project to deal with Go templating. Feel free to clone this and open the project - we will focus on layouts/index.html, which gets its data from content/ You can copy and paste any code examples into the index page’s HTML to see the results.


Go templating makes it easy to save data to variables, which we can use later in our code. Hugo has many built-in variables that are useful, but here are some you’ll often use for pages and the global site:

  • site/.Site*: any data that lives in the global site scope, often in our config.toml or data files. The most common example is site.Title - the name of our site in config.toml. Pages, sections, data, and other parts of a site can also be accessed at site level. Our baseof.html file already uses a small amount of Go templating.
  • .Params: when used in a layout page, this refers to the parameters (data) available to that page - generally the front matter from page content. We’ve set up some front matter for this page, so let’s use it here.

A Go template variable follows this syntax:

$myVariable := "data"

So, let’s get some example data from our front matter, and set it to a variable in layouts/index.html:

{{ $animals := .Params.animals }}
{{ $animals }}

Now we have a list of animals from front matter in our content folder (more on this later). However, the data needs to unpacked a bit more - a good place to use a loop.

Be aware that there are two versions of “site”: site (a global function) and .Site (a page variable). Using .Site inside a partial will likely not work as it might not know which page you are referring to, so use site or pass in a dictionary of data. However, the .Site variable also has some specific variables available to it that can be helpful when not using partials.

Loops - range

Most programming languages have a way of “looping” through lists/arrays of items. Go templating is no different, but instead of the common for or while loops, it’s range. Let’s display the animal names in our $animals variable on our page:

{{ range $animals }}
  <p>{{ .name }}</p>
{{ end }}

Elegant and simple: just identify your list of items, and the “.” context will refer to the current item when Hugo puts each quote onto the page.

You might also want to get the “index” from loops, so you can put a number next to each loop item:

{{ range $index, $animal := $animals }}
  <p>{{ add $index 1 }}: {{ $ }}</p>
{{ end }}

$index is just the name we’ve decided on; you can name it anything, but it’ll start from zero (hence adding 1).

Logic - if, else, comparisons

No language would be complete without a way to control the flow of data. To do this, we use logic statements such as if and else, often combined with comparison operators (which are a bit different in Go templating):

Operator Meaning
eq Equals
ne Not equals
lt Less than
gt Greater than
ge Greater than or equal to
le Less than or equal to

Syntax for usage:

{{ if <comparison operator> <compare this...> <...with this> }}
  {{*/ what should happen */}}
{{ else if <more logic> }}
  {{*/ what should happen otherwise /*}}
{{ else }}
  {{*/ in any other case /*}}
{{ end }}

Let’s put these into action! We’ve got our list of animals available, but it also includes data about whether the animal is a bird or not. We can use this to create some basic logic in our loops:

{{ range $index, $animal := .Params.animals }}
  {{ if eq $animal.is_bird true }}
    <p>A {{$}} is a bird.</p>
  {{ else }}
    <p>A {{$}} is not a bird.</p>
  {{ end }}
{{ end }}

Built-in data structures: slices and dictionaries

Another great feature of Go templating is the ability to create dictionaries (a.k.a hashes/maps) and slices (a.k.a arrays). These make it easier to create and pass around data. In either case, the syntax is similar:

<!-- slice = array of ["kea", "kaka", "tui"] -->
{{ $s := slice "kea" "kaka" "tui" }}

<!-- dictionary: "key" "value" -->
{{ $d := dict "year" (now.Format "2006")}}

Items in slices and dictionaries should be space-separated - not comma separated.

Hugo built-in functions

Hugo provides a large number of built-in functions to help you manage content. The logical operators above are actually also functions. Here’s a quick overview of some of the most common functions you’ll use, and what they do - feel free to copy and paste examples:

Useful Hugo functions
Function name Purpose Example Output
isset Checks whether a parameter exists {{ if isset .Params "description" }}

{{ .Params.description }}

{{ end}}
true if exists, false if not
in Checks if something is present in another thing {{ if in .Params.mascot "goose" }}

{{ .Params.mascot }}

{{ end }}
true if exists, false if not
.Format Format a specific date {{ now.Format "2006"}} Current year, e.g., 2021
print Create a string or concatenate (glue together) multiple {{ print "mysite/blog" "/first-article" }} "mysite/blog/first-article"
sort Sort a group of items - can specify by what {{ $sorted := sort (slice "b" "c" "a") }} {{ $sorted }} ["a", "b", "c" ]
replace Replace something inside a string {{ replace "gaase" "a" "o" }} "goose"
delimit Join together multiple pieces of text, with a character of choice (e.g., empty space, comma) {{ delimit (slice "A" "whole" "sentence") " " }} "A whole sentence"
append Append an element to a slice {{ $empty := slice }} {{ $notEmpty := $empty | append "Not empty"}} {{$notEmpty}} [Not empty]
where Filter an array to find only elements that fit your case {{ $birds := where .Params.animals "is_bird" true }} {{ $birds }} Only the birds from the array

Functions that are lowercase (e.g. eq) are used without the dot context. Those that are uppercase must be used with the dot context (e.g. .Format).

What's next?

This was simply a crash course in the basics of Go templating. Hugo’s vast documentation is too much to cover in one lesson, so feel free to refer to that for specific definitions and examples. In the next lesson, we will be providing useful examples of Go templating in page layouts, posts, and more.

Our next lesson focuses on fragmenting our site with partials so that we can make our site even more manageable and avoid repeating ourselves.