logo

Go Templates

This cheatsheet covers the syntax and features of Go's built-in template engines: text/template for general text generation and html/template for generating HTML securely (preventing XSS attacks). html/template has the same syntax but adds automatic contextual escaping.


Basic Syntax

  • Delimiters: Actions are enclosed in {{ and }}.
    Hello, {{ .Name }}!
    
  • Comments: {{/* This is a comment */}} Comments are not rendered.

Pipelines & Data Output (dot .)

  • The Cursor (.): Represents the current data context being evaluated. Initially, it's the data object passed to Execute.
    {{ . }}         // Renders the current data object itself (if printable)
    
  • Accessing Fields: Use dot notation for struct fields or map keys.
    {{ .FieldName }} // Accesses FieldName of a struct
    {{ .MapKey }}    // Accesses key "MapKey" in a map
    
    • Field/key names must typically be exported (start with an uppercase letter) if accessing struct fields from a different package.
  • Pipelines (|): Chain commands together. The output of one command becomes the input for the next. Often used with functions.
    {{ .Value | printf "%.2f" }} // Formats .Value using printf function
    {{ . | len }}              // Gets the length of the current data (if applicable)
    
  • Output: The final result of a pipeline is printed to the output.

Actions (Control Structures)

Variables

  • Declaration: Assign the result of a pipeline to a variable using :=. Variables start with $.

    {{ $name := .Username }}
    Hello, {{ $name }}!
    
    {{ $length := .Items | len }}
    You have {{ $length }} items.
    
  • Scope: Variables are scoped to the block they are defined in (if, with, range).

Conditionals (if/else)

  • Basic if: Executes block if pipeline is "truthy" (not false, 0, nil, empty string/slice/map).
    {{ if .IsActive }}
      User is active.
    {{ end }}
    
  • if/else:
    {{ if .HasPermission }}
      Access Granted.
    {{ else }}
      Access Denied.
    {{ end }}
    
  • if/else if/else:
    {{ if eq .Status "pending" }}
      Waiting...
    {{ else if eq .Status "completed" }}
      Done!
    {{ else }}
      Status: {{ .Status }}
    {{ end }}
    
  • Boolean Operators: Use built-in functions like and, or, not, eq (equal), ne (not equal), lt (less than), le (less than or equal), gt (greater than), ge (greater than or equal).
    {{ if and .IsLoggedIn (not .IsAdmin) }}
      Welcome, user!
    {{ end }}
    

Iteration (range)

  • Iterates over slices, arrays, maps, or channels. The . context is set to the current element for each iteration.

    // Iterate over slice/array
    {{ range .Items }}
      - {{ . }} // '.' is the current item
    {{ end }}
    
    // Get index and element
    {{ range $index, $element := .Items }}
      {{ $index }}: {{ $element }}
    {{ end }}
    
    // Iterate over map
    {{ range $key, $value := .MapData }}
      {{ $key }} -> {{ $value }}
    {{ end }}
    
    // Access original dot inside range
    {{ $outerDot := . }}
    {{ range .Items }}
      Item: {{ . }}, Outer Value: {{ $outerDot.SomeOuterField }}
    {{ end }}
    
  • range/else: Executes else block if the collection is empty or nil.

    {{ range .Items }}
      - {{ . }}
    {{ else }}
      No items found.
    {{ end }}
    

Setting Context (with)

  • Sets the . context to the result of the pipeline for the block. Useful for simplifying access or checking for nil.
    {{ with .User }}
      Username: {{ .Name }}
      Email: {{ .Email }}
    {{ end }}
    
  • with/else: Executes else block if the pipeline evaluates to false/zero/nil/empty.
    {{ with .OptionalData }}
      Data: {{ .Value }}
    {{ else }}
      No optional data provided.
    {{ end }}
    

Functions

  • Functions are called within actions: {{ functionName arg1 arg2 ... }} or as part of a pipeline {{ . | functionName arg1 }}.

Built-in Functions (Common Examples)

| Function | Description | Example | | :------------- | :-------------------------------------------------------------------------------------------- | :------------------------------------------------ | -------------- | | and | Logical AND (takes multiple args). | {{ and .Val1 .Val2 }} | | or | Logical OR (takes multiple args). | {{ or .Val1 .Val2 }} | | not | Logical NOT (takes one arg). | {{ not .Val }} | | len | Returns the length of its argument (string, slice, map, etc.). | {{ len .Items }} | | index | Access element by index/key (index collection key/index). | {{ index .Users 0 }} / {{ index .Map "key" }} | | print | Returns string representation of args (no spaces). | {{ print "Value: " .Val }} | | println | Returns string representation of args (adds spaces, newline). | {{ println "Value:" .Val }} | | printf | Formatted printing (like fmt.Sprintf). | {{ printf "User %s (ID: %d)" .Name .ID }} | | html | (html/template only) Returns arg as trusted HTML (no escaping). Use with care! | {{ html .UnescapedHTML }} | | js | (html/template only) Returns arg properly escaped for JS. | var name = {{ .Name | js }}; | | urlquery | (html/template only) Returns arg properly escaped for URL query. | <a href="/search?q={{ .Term | urlquery }}"> | | call | Calls a function/method with args (call Func arg1 arg2 ...). | {{ call .CalculateBonus .Salary }} | | Comparison | eq, ne, lt, le, gt, ge (Equal, Not Equal, Less, LessEqual, Greater, GreaterEqual) | {{ if eq .Status "Active" }} |

Custom Functions

  • Add custom functions to a template using the .Funcs method before parsing.

    import "text/template"
    import "strings"
    
    funcMap := template.FuncMap{
        "upper": strings.ToUpper,
        "add": func(a, b int) int { return a + b },
    }
    
    tmpl, err := template.New("custom").Funcs(funcMap).Parse(`{{ . | upper }}`)
    // ... or ...
    tmpl = template.Must(template.New("custom").Funcs(funcMap).Parse(`Sum: {{ add 10 5 }}`))
    
    // In template:
    {{ "hello" | upper }} // -> HELLO
    {{ add 3 4 }}       // -> 7
    

Template Definition & Nesting

  • Define (define): Define a named template block.

    {{ define "userCard" }}
      <div>
        <h2>{{ .Name }}</h2>
        <p>{{ .Email }}</p>
      </div>
    {{ end }}
    
  • Execute (template): Execute a defined template, optionally passing data (pipeline sets the . for the called template).

    {{/* Execute userCard with the current dot */}}
    {{ template "userCard" . }}
    
    {{/* Execute userCard with a specific user */}}
    {{ range .Users }}
      {{ template "userCard" . }}
    {{ end }}
    
  • Block (block): Defines a template block that can be overridden by child templates (useful for inheritance). Acts like define if not overridden.

    {{/* base.html */}}
    <html>
    <head><title>{{ block "title" . }}Default Title{{ end }}</title></head>
    <body>{{ block "content" . }}Default Content{{ end }}</body>
    </html>
    
    {{/* child.html - Parsed *with* base.html */}}
    {{ define "title" }}My Page Title{{ end }}
    {{ define "content" }}<p>This is my page content.</p>{{ end }}
    

    When executing child.html (which must have been parsed along with base.html), the blocks defined in child.html will replace those in base.html.


Whitespace Control

  • Use a hyphen - inside the delimiters to trim whitespace:
    • {{- Trim preceding whitespace.
    • -}} Trim trailing whitespace.
    {{ range .Items -}}
      {{/* No newline before item */}}
      - {{ . }}
      {{- /* No newline after item */}}
    {{- end }}
    
    This example aims to produce - item1- item2- item3.

html/template Specifics

  • Contextual Auto-Escaping: The primary feature. Automatically escapes data based on the context it's being inserted into (HTML body, HTML attributes, URLs, JavaScript, CSS).

    <!-- If .UserInput is "<script>alert(1)</script>" -->
    
    <p>{{ .UserInput }}</p>
    <!-- Renders: <p><script>alert(1)</script></p> -->
    
    <a href="{{ .UserURL }}">Link</a>
    <!-- If .UserURL is "javascript:alert(1)", renders: <a href="#ZgotmplZ">Link</a> -->
    
    <div data-info="{{ .Data }}"></div>
    <!-- Renders: <div data-info="<script>..."></div> -->
    
    <script>
      var name = {{ .UserName }};
    </script>
    <!-- Renders: var name = "\u003cscript..."; -->
    
  • Security: Designed to prevent Cross-Site Scripting (XSS) by default.

  • Trusted Content Types: In Go code, wrap trusted (already safe/sanitized) content in types like template.HTML, template.CSS, template.JS, template.URL before passing it to the template to bypass escaping. Use with extreme caution.

    data := struct{ SafeHTML template.HTML }{ SafeHTML: template.HTML("<b>Bold Text</b>") }
    tmpl.Execute(w, data)
    
    {{ .SafeHTML }}
    <!-- Renders: <b>Bold Text</b> -->
    

Basic Go Usage Example (text/template)

package main

import (
	"os"
	"text/template"
)

type User struct {
	Name   string
	Active bool
	Emails []string
}

func main() {
	user := User{
		Name:   "Alice",
		Active: true,
		Emails: []string{"[email protected]", "[email protected]"},
	}

	// Template definition (can also load from file)
	tmplString := `Hello, {{ .Name }}!
{{ if .Active }}
  You are active.
  Emails:
  {{ range $i, $email := .Emails -}}
    {{ $i }}: {{ $email }}
  {{ else -}}
    No emails listed.
  {{ end -}}
{{ else }}
  Your account is inactive.
{{ end }}
`

	// Create a new template and parse the definition.
	tmpl, err := template.New("userGreeting").Parse(tmplString)
	if err != nil {
		panic(err) // Handle error appropriately
	}

	// Execute the template with the data and write to standard output.
	err = tmpl.Execute(os.Stdout, user)
	if err != nil {
		panic(err) // Handle error appropriately
	}
}