Zalgorithm

Go template basics

Hugo uses the Go text/template and html/template packages. The text/template package is for generating textual output. The html/template package is for generating HTML output (safe against code injection).

A rough overview of Go HTML templates

The templates are made up of HTML and actions or template actions. Actions are wrapped in double curly braces: {{ }}.

Taking {{ .LinkTitle | lower }} as an example:

The context or cursor

. (the dot) is the context or cursor. It represents the current data object that’s being worked with.

Methods and fields

LinkTitle is a method that’s associated with a struct that computes or returns something. For example, in Go, the Page struct might look like this (note that the Hugo code is here: https://github.com/gohugoio/hugo ):

type Page struct {
    Title string        // Title is a field
    Date  time.Time     // Date is a field
}

The LinkTitle method (is likely) associated with the Page struct. Something like this:

func (p *Page) LinkTitle() string {
    // Does some processing and returns a string
    return processTitle(p.Title)
}

The action might also contain a field. For example, if . is a Page, the Title field might be accessed like this:

{{ .Title }}

The pipe operator

The action {{ .LinkTitle | lower }} contains a pipe. The pipe operator (|) passes the output that’s on the left as an input to what’s on the right.

The use of the term pipeline in the Go template documentation

This has confused me a few times:

{{range pipeline}} T1 {{end}}
 The value of the pipeline must be an array, slice, map, iter.Seq,
 iter.Seq2, integer or channel.

It would probably help to read the docs from the start. I don’t think pipeline is being used in the same sense as the pipe operator. From the docs :

A pipeline is a possibly chained sequence of “commands”. A command is a simple value (argument) or a function or method call, possibly with multiple arguments…

In this context “pipeline” means an expression that produces a value.

The term pipeline also seems to get used to refer to functions that are chained together with the pipe operator.

Functions

In the action {{ .LinkTitle | lower }}, lower is a function. It’s a buit-in Hugo/Go template function that converts text to lowercase.

Variable assignment

A variable assignment example from the Hugo terms.html template:

{{- $page := .page }} {{- $taxonomy := .taxonomy }}
{{- with $page.GetTerms $taxonomy }}
  {{- $label := (index . 0).Parent.LinkTitle }}
  <div class="terms">
    <div>{{ $label }}:</div>
    <ul>
      {{- range . }}
        <li><a href="{{ .RelPermalink }}">{{ .Name }}</a></li>
      {{- end }}
    </ul>
  </div>
{{- end }}

In the code above the := operator is used to assign .page to $page and .taxonomy to $taxonomy.

Is the lack of whitespace between the cursor and methods significant?

The lack of whitespace between . and LinkTitle is not significant. It’s a coding convention.

Whitespace control

Whitespace control is done with hyphens (-) at action boundaries:

{{- .LinkTitle -}}  // trims whitespace before and after
{{- .LinkTitle }}  // trims whitespace before
{{ .LinkTitle -}}  // trims whitespace after

Conditional code execution

Similar to normal code:

{{ if .Title }}
  <p>{{ .Title }}
{{ else }}
  <p>This page doesn't have a title
{{ end }}

The range function

{{- range . }}
  {{ .Name }}
{{- end }}

In the code above, . is the “pipeline” in the technical sense that I referred to above. It’s the expression that produces the collection to range over. The cursor (.) expression is the current context.

with statements and blocks

{{ with pipeline }}
  T1
{{ end }}

Where pipeline is an expression that evaluates to a value. If pipeline is not empty (non-zero, non-nil, non-empty slice/map/string), the current context (.) will be set to the value of pipeline and template T1 will be evaluated with that context.

The pattern could also be written as:

{{ with pipeline }}
  T1
{{ else }}
  T2
{{ end }}

With the {{ else }} action, if pipeline is empty, the context will stay the same as it was before the beginning of the with block and template T2 will be evaluated with that context.

The significance of the dollars symbol in a with block

The dollar symbol ($) is a special variable that is automatically assigned to the initial value of the context when a template’s evaluation begins. See notes / Link render hooks for details about what got me looking at this.

An actual code example:

{{- else }}
  {{- with $u.Path }}
    {{- with $p := or ($.PageInner.GetPage .) ($.PageInner.GetPage (strings.TrimRight "/" .)) }}
      {{- /* Destination is a page. */}}
      {{- $href := .RelPermalink }}
      {{- with $u.RawQuery }}
        {{- $href = printf "%s?%s" $href . }}
      {{- end }}

The code makes sense up to ($.PageInner.GetPage .). The current context is $u.Path. That’s just a string (I think). It doesn’t have a PageInner field (I checked). It turns out that the dollars sign ($) has a special meaning in with blocks. When you enter the block the cursor (.) is rebound to the value of the pipeline. The $ symbol is a special variable that is (automatically (?)) assigned to the initial value of the dot (.) when template execution begins.

Declaring initializing and reassigning variables in Go templates

:= versus =

{{- $href := .RelPermalink }}        {{- /* Declaration - creates $href */}}
{{- with $u.RawQuery }}
  {{- $href = printf "%s?%s" $href . }}  {{- /* Reassignment - modifies existing $href */}}
{{- end }}
{{- with $u.Fragment }}
  {{- $href = printf "%s#%s" $href . }}  {{- /* Reassignment again */}}
{{- end }}

Note that this is somewhat different than the rules in regular Go code.

References

Go Standard Library Documentation. “text/template.” December 2, 2025. https://pkg.go.dev/text/template .

Go Standard Library Documentation. “html/template.” December 2, 2025. https://pkg.go.dev/html/template .

Hugo Documentation. “Introduction to templating.” November 16, 2025. https://gohugo.io/templates/introduction/ .

Tags: