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
: Executeselse
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
: Executeselse
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 likedefine
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 withbase.html
), the blocks defined inchild.html
will 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.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
}
}