html/template is text/template's API plus automatic, context-aware escaping.
Every action, method, and built-in function is identical to text/template. Only the import path changes.
import "html/template" // was "text/template"
t := template.Must(template.New("page").Parse(src))Context-Aware Escaping
html/template escapes every value for its context: HTML text, an attribute, JavaScript, CSS, or a URL.
<p>{{.Text}}</p> // HTML text
<a href="{{.URL}}"> // HTML attribute
<script>var x = {{.Value}};</script> // JavaScripttemplate.HTML marks a string as already vetted, safe to insert unescaped.
var trusted = template.HTML(renderedMarkdown)
tmpl.Execute(w, trusted)Composing Templates
{{define "name"}}...{{end}} declares a named block. {{template "content" .}} inserts one named block inside another.
const layoutHTML = `{{define "layout"}}<!doctype html>
<html><body>
{{template "content" .}}
</body></html>{{end}}`
const contentHTML = `{{define "content"}}<h1>{{.Title}}</h1>
<p>{{.Body}}</p>{{end}}`Parse multiple templates onto the same *Template value to combine them. ExecuteTemplate runs one by name.
type Page struct{ Title, Body string }
tmpl := template.Must(template.New("layout").Parse(layoutHTML))
tmpl = template.Must(tmpl.Parse(contentHTML))
tmpl.ExecuteTemplate(w, "layout", Page{Title: "Hello", Body: "First post."})Examples
The same template and input, run through each package. Only the import changes.
malicious := "<script>alert(1)</script>"
txt := texttemplate.Must(texttemplate.New("t").Parse("{{.}}"))
txt.Execute(os.Stdout, malicious)
fmt.Println()
html := template.Must(template.New("t").Parse("{{.}}"))
html.Execute(os.Stdout, malicious)<script>alert(1)</script>
<script>alert(1)</script>A plain string is always escaped. template.HTML marks a value as already vetted.
t := template.Must(template.New("trusted").Parse("<div>{{.}}</div>"))
t.Execute(os.Stdout, "<b>bold</b>")
fmt.Println()
t.Execute(os.Stdout, template.HTML("<b>bold</b>"))<div><b>bold</b></div>
<div><b>bold</b></div>A FuncMap function returning template.HTML is trusted like a literal value. Escape the input inside the function first.
funcs := template.FuncMap{
"bold": func(s string) template.HTML {
return template.HTML("<b>" + template.HTMLEscapeString(s) + "</b>")
},
}
t := template.Must(template.New("card").Funcs(funcs).Parse("<p>{{.Name | bold}}</p>"))
t.Execute(os.Stdout, struct{ Name string }{"O'Brien"})<p><b>O'Brien</b></p>Same API as text/template
| Function | Description |
|---|---|
New(name string) *Template template.New("page") | Creates a new, undefined template with the given name |
Must(t *Template, err error) *Template template.Must(template.New("t").Parse(s)) | Panics if err is non-nil, otherwise returns t; identical to text/template's Must |
ParseFiles(filenames ...string) / ParseGlob(pattern string) / ParseFS(fsys fs.FS, patterns ...string) template.ParseFiles("page.html") | Parse templates from files, a glob pattern, or an fs.FS; identical to their text/template counterparts |
(*Template) Parse(text string) (*Template, error) t.Parse(src) | Parses text as the template body |
(*Template) Execute(wr io.Writer, data any) error t.Execute(w, data) | Renders the template to wr, escaping every action's output for the context it lands in |
(*Template) ExecuteTemplate(wr io.Writer, name string, data any) error t.ExecuteTemplate(w, "page", data) | Like Execute, but runs a specific associated template by name |
(*Template) Funcs(funcMap FuncMap) *Template t.Funcs(fm) | Registers custom functions callable from the template |
(*Template) New / Clone / Lookup / Templates / DefinedTemplates / Name / Option / Delims t.Clone() | Template composition and introspection, identical to text/template |
Contextual escaping
| Function | Description |
|---|---|
HTML text context <p>{{.Comment}}</p> | Escapes <, >, &, ", and ' so data can't inject a new tag or attribute |
HTML attribute context <a href="{{.URL}}"> | Escapes quotes and special characters so data can't break out of the attribute value |
JavaScript context <script>var x = {{.Value}};</script> | Escapes for a JS string or number literal, inside a <script> block or an event handler attribute |
CSS context <div style="color:{{.Color}}"> | Escapes for a CSS value inside a <style> block or a style attribute |
URL context <a href="{{.Link}}"> | Validates the scheme and percent-encodes the value |
Trusted content types
| Function | Description |
|---|---|
HTML template.HTML("<b>bold</b>") | Marks a string as safe, pre-vetted markup to insert unescaped |
HTMLAttr template.HTMLAttr("dir=\"ltr\"") | Marks a string as a safe, complete HTML attribute |
JS template.JS("1+1") | Marks a string as safe JavaScript to insert unescaped inside a <script> |
JSStr template.JSStr("a\\nb") | Marks a string as the safe contents of a JS string literal, without the surrounding quotes |
CSS template.CSS("color: red") | Marks a string as safe CSS to insert unescaped |
URL template.URL("/search?q=go") | Marks a string as a safe, complete URL |
Srcset template.Srcset("img.png 1x") | Marks a string as a safe value for an <img> srcset attribute |
HTMLEscapeString(s string) string template.HTMLEscapeString(s) | Escapes text for safe HTML output, for use outside template execution |
JSEscapeString(s string) string template.JSEscapeString(s) | Escapes text for safe inclusion in a JS string literal, for use outside template execution |
URLQueryEscaper {{.Query | urlquery}} | A template function that escapes text for a URL query string |
Template actions
| Function | Description |
|---|---|
{{.}} {{.}} | The current data value |
{{.Field}} / {{.Method}} {{.Name}} | A field or zero-argument method of the current data value |
{{if pipeline}}...{{else}}...{{end}} {{if .Active}}yes{{end}} | Renders the block only if pipeline's result is truthy |
{{range pipeline}}...{{end}} {{range .Items}}{{.}}{{end}} | Repeats the block once per element, setting . to each in turn |
{{with pipeline}}...{{end}} {{with .User}}{{.Name}}{{end}} | Sets . to pipeline's result inside the block, skipping it entirely if the value is empty |
{{define "name"}}...{{end}} {{define "row"}}...{{end}} | Declares a named, reusable template within the same file |
{{template "name" pipeline}} {{template "row" .}} | Executes the named template, passing pipeline as its data |
{{block "name" pipeline}}...{{end}} {{block "row" .}}default{{end}} | Defines a template and executes it immediately, as a default another file's {{define}} can override |
{{/* comment */}} {{/* TODO */}} | A comment; produces no output |
{{pipeline | function}} {{.Name | upper}} | Pipes a value into a function or another pipeline stage, left to right |
Built-in functions
| Function | Description |
|---|---|
and / or / not {{if and .A .B}} | Boolean logic over their arguments |
eq / ne / lt / le / gt / ge {{if eq .Status "ok"}} | Comparison functions usable inside a pipeline |
len {{len .Items}} | Length of its argument: string, slice, map, or array |
index {{index .Items 0}} | Indexes into a slice, array, or map |
print / printf / println {{printf "%.2f" .Price}} | fmt.Sprint, fmt.Sprintf, and fmt.Sprintln, usable inside a template |
call {{call .Fn .Arg}} | Calls a function value with the given arguments |