There are two packages that support templating in Go. One package is for text templating and the other is for HTML templating. Both packages are very similar and support the same syntax. The main difference between the two packages is that the html/template package will automatically escape HTML characters to prevent XSS attacks, while the text/template package does not provide this security feature.

Basic Template Usage

package main

import (
    "os"
    "text/template"
)

func main() {
    tmpl := template.Must(template.New("example").Parse("Hello, !"))
    tmpl.Execute(os.Stdout, struct{ Name string }{"World"})
    // Output: Hello, World!
}

Variables

Go templates support variables. Variables are defined using the following syntax.

{{- $variableName := .variableValue -}}

Conditional Branching

Golang templates support conditional branching using the if, else and end keywords.

{{- if .variableValue -}}

{{- else -}}

{{- endif -}}
Function / Operator Description Example
eq Equals, arg1 == arg2 {{ if eq arg1 arg2 }} … {{ endif }}
ne Not Equals, arg1 != arg2 {{ if ne arg1 arg2 }} … {{ endif }}
lt Less Than, arg1 < arg2 {{ if lt arg1 arg2 }} … {{ endif }}
le Less Than or Equal To, arg1 <= arg2 {{ if le arg1 arg2 }} … {{ endif }}
gt Greater Than, arg1 > arg2 {{ if gt arg1 arg2 }} … {{ endif }}
ge Greater Than or Equal To, arg1 >= arg2 {{ if ge arg1 arg2 }} … {{ endif }}

Iteration

Golang templates support iteration using the range keyword. The range action allows you to iterate over arrays, slices, maps, channels, or strings.

{{- range .variableValue -}}
  <!-- Inside the range, "." is set to the current item -->
  {{ . }}
{{- end -}}

Accessing Index and Value

For slices and arrays, you can access both the index and value:

{{- range $index, $element := .Slice -}}
  <!-- $index contains the current position -->
  <!-- $element contains the value -->
  {{ $index }}: {{ $element }}
{{- end -}}

Iterating Over Maps

For maps, you get key-value pairs:

{{- range $key, $value := .Map -}}
  {{ $key }}: {{ $value }}
{{- end -}}

Example: Rendering a List

<ul>
{{- range .Items -}}
  <li>{{ .Name }}: {{ .Price }}</li>
{{- end -}}
</ul>

Nested Ranges

You can nest range actions for multi-dimensional data:

{{- range .Sections -}}
  <h2>{{ .Title }}</h2>
  <ul>
  {{- range .Items -}}
    <li>{{ .Name }}</li>
  {{- end -}}
  </ul>
{{- end -}}

Empty Check with else

You can handle empty collections with an else clause:

{{- range .Items -}}
  <!-- This executes for each item -->
  {{ .Name }}
{{ else }}
  <!-- This executes if .Items is empty or nil -->
  No items found
{{- end -}}

Breaking Out of a Range

Go templates don’t support a break statement, but you can use with/if combinations for conditional processing:

{{- range .Items -}}
  {{- if .ShouldProcess -}}
    {{ .Name }}
  {{- end -}}
{{- end -}}

Functions

Golang templates support functions. The following functions are available by default:

Function Description Example
and Returns the boolean AND of its arguments ...
or Returns the boolean OR of its arguments ...
not Returns the boolean NOT of its argument ...
len Returns the length of a string, slice, map or array ``
index Returns the result of indexing its first argument by the following arguments ``
print Formats using default formats ``
printf Formats according to a format specifier ``
println Like print but adds a newline ``
html Safely escapes for HTML ``
js Safely escapes for JavaScript ``
call Calls the first argument with the remaining arguments as parameters ``

Function Examples

Using len with conditionals:

{{ if eq (len .Items) 0 }}
    <p>No items found</p>
{{ else }}
    <p>Found {{ len .Items }} items</p>
{{ end }}

Using index to access array elements:

{{ index .Array 0 }} <!-- First element -->
{{ index .NestedMap "key" "subkey" }} <!-- Nested access -->

String formatting with printf:

{{ printf "Name: %-10s Age: %3d" .Name .Age }}

Adding Custom Functions

You can extend templates with custom functions by using the FuncMap:

package main

import (
    "html/template"
    "os"
    "strings"
    "time"
)

func main() {
    // Create a custom function map
    funcMap := template.FuncMap{
        "upper":    strings.ToUpper,
        "formatDate": func(t time.Time) string {
            return t.Format("2006-01-02")
        },
        "add": func(a, b int) int {
            return a + b
        },
    }
    
    // Create a template with the function map
    tmpl := template.New("example").Funcs(funcMap)
    
    // Parse template with function usage
    tmpl, err := tmpl.Parse(`
        Upper: 
        Date: 
        Sum: 
    `)
    
    if err != nil {
        panic(err)
    }
    
    // Execute template
    data := struct {
        Date time.Time
    }{
        Date: time.Now(),
    }
    
    tmpl.Execute(os.Stdout, data)
}

Output:

Upper: HELLO
Date: 2023-10-15
Sum: 15