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 toExecute.{{ . }} // 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: Executeselseblock 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: Executeselseblock 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
.Funcsmethod 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 likedefineif 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 withbase.html), the blocks defined inchild.htmlwill replace those inbase.html.
Whitespace Control
- Use a hyphen
-inside the delimiters to trim whitespace:{{-Trim preceding whitespace.-}}Trim trailing whitespace.
This example aims to produce{{ range .Items -}} {{/* No newline before item */}} - {{ . }} {{- /* No newline after item */}} {{- end }}- 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.URLbefore 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
}
}