Documentation Index
Fetch the complete documentation index at: https://mintlify.com/golang/go/llms.txt
Use this file to discover all available pages before exploring further.
The html package provides functions for escaping and unescaping HTML text, and the html/template subpackage provides data-driven templates for generating HTML output safe against code injection.
html Package
HTML Escaping
import "html"
func escapeExample() {
raw := `<script>alert("XSS")</script>`
escaped := html.EscapeString(raw)
fmt.Println(escaped)
// Output: <script>alert("XSS")</script>
unescaped := html.UnescapeString(escaped)
fmt.Println(unescaped)
// Output: <script>alert("XSS")</script>
}
html/template Package
Provides data-driven templates with automatic contextual escaping.
Basic Template
import "html/template"
func basicTemplate() {
tmpl := template.Must(template.New("greeting").Parse(`
<h1>Hello, {{.Name}}!</h1>
<p>You are {{.Age}} years old.</p>
`))
data := struct {
Name string
Age int
}{"Alice", 30}
tmpl.Execute(os.Stdout, data)
}
Template from File
func fileTemplate() error {
tmpl, err := template.ParseFiles("templates/index.html")
if err != nil {
return err
}
data := map[string]interface{}{
"Title": "My Page",
"User": "Alice",
}
return tmpl.Execute(os.Stdout, data)
}
Multiple Templates
func multipleTemplates() error {
tmpl, err := template.ParseGlob("templates/*.html")
if err != nil {
return err
}
// Execute specific template
return tmpl.ExecuteTemplate(os.Stdout, "index.html", data)
}
Template Syntax
Variables
{{.}} <!-- Current context -->
{{.Name}} <!-- Field access -->
{{.User.Email}} <!-- Nested field -->
Conditionals
{{if .IsLoggedIn}}
<p>Welcome back!</p>
{{else}}
<p>Please log in.</p>
{{end}}
{{if and .User .User.IsActive}}
<p>Active user</p>
{{end}}
Loops
<ul>
{{range .Items}}
<li>{{.}}</li>
{{end}}
</ul>
{{range $index, $item := .Items}}
<p>{{$index}}: {{$item}}</p>
{{end}}
{{range .Users}}
<div>{{.Name}} - {{.Email}}</div>
{{else}}
<p>No users found</p>
{{end}}
With
{{with .User}}
<p>Name: {{.Name}}</p>
<p>Email: {{.Email}}</p>
{{end}}
Variables
{{$title := .Title}}
<h1>{{$title}}</h1>
{{range $i, $item := .Items}}
<p>Item {{$i}}: {{$item}}</p>
{{end}}
Template Inclusion
{{template "header" .}}
<main>
Content here
</main>
{{template "footer" .}}
Define and Block
{{define "header"}}
<header>
<h1>{{.Title}}</h1>
</header>
{{end}}
{{block "content" .}}
Default content
{{end}}
Template Functions
Built-in Functions
<!-- Comparison -->
{{if eq .Status "active"}}Active{{end}}
{{if ne .Count 0}}Has items{{end}}
{{if lt .Age 18}}Minor{{end}}
{{if gt .Score 90}}Excellent{{end}}
<!-- Logic -->
{{if and .User .User.IsActive}}...{{end}}
{{if or .IsAdmin .IsModerator}}...{{end}}
{{if not .IsDeleted}}...{{end}}
<!-- String operations -->
{{print .Name}}
{{printf "%s: %d" .Label .Count}}
<!-- Indexing -->
{{index .Items 0}}
{{index .Map "key"}}
<!-- Length -->
{{len .Items}}
Custom Functions
func customFunctions() {
funcMap := template.FuncMap{
"upper": strings.ToUpper,
"add": func(a, b int) int {
return a + b
},
"formatDate": func(t time.Time) string {
return t.Format("2006-01-02")
},
}
tmpl := template.New("test").Funcs(funcMap)
tmpl.Parse(`
<p>{{upper .Name}}</p>
<p>Total: {{add .A .B}}</p>
<p>Date: {{formatDate .Created}}</p>
`)
}
Practical Examples
Web Page Template
type PageData struct {
Title string
User *User
Posts []Post
CurrentYear int
}
func renderPage(w http.ResponseWriter, data PageData) error {
tmpl := template.Must(template.ParseFiles(
"templates/layout.html",
"templates/header.html",
"templates/footer.html",
"templates/post-list.html",
))
return tmpl.ExecuteTemplate(w, "layout.html", data)
}
layout.html:
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
</head>
<body>
{{template "header" .}}
<main>
{{template "post-list" .}}
</main>
{{template "footer" .}}
</body>
</html>
post-list.html:
{{define "post-list"}}
<div class="posts">
{{range .Posts}}
<article>
<h2>{{.Title}}</h2>
<p>{{.Content}}</p>
<small>By {{.Author}} on {{.Date}}</small>
</article>
{{else}}
<p>No posts available.</p>
{{end}}
</div>
{{end}}
Email Template
type EmailData struct {
RecipientName string
Subject string
Message string
SenderName string
UnsubscribeURL string
}
func generateEmail(data EmailData) (string, error) {
tmpl := template.Must(template.New("email").Parse(`
Subject: {{.Subject}}
Dear {{.RecipientName}},
{{.Message}}
Best regards,
{{.SenderName}}
---
To unsubscribe: {{.UnsubscribeURL}}
`))
var buf bytes.Buffer
err := tmpl.Execute(&buf, data)
return buf.String(), err
}
type FormData struct {
Values map[string]string
Errors map[string]string
}
func renderForm(w http.ResponseWriter, data FormData) {
tmpl := template.Must(template.New("form").Parse(`
<form method="POST">
<div>
<label>Name:</label>
<input type="text" name="name" value="{{.Values.name}}">
{{if .Errors.name}}
<span class="error">{{.Errors.name}}</span>
{{end}}
</div>
<div>
<label>Email:</label>
<input type="email" name="email" value="{{.Values.email}}">
{{if .Errors.email}}
<span class="error">{{.Errors.email}}</span>
{{end}}
</div>
<button type="submit">Submit</button>
</form>
`))
tmpl.Execute(w, data)
}
Template Caching
type TemplateCache struct {
templates map[string]*template.Template
mu sync.RWMutex
}
func NewTemplateCache() *TemplateCache {
return &TemplateCache{
templates: make(map[string]*template.Template),
}
}
func (tc *TemplateCache) Get(name string) (*template.Template, error) {
tc.mu.RLock()
tmpl, ok := tc.templates[name]
tc.mu.RUnlock()
if ok {
return tmpl, nil
}
tc.mu.Lock()
defer tc.mu.Unlock()
// Double-check
if tmpl, ok := tc.templates[name]; ok {
return tmpl, nil
}
// Parse and cache
tmpl, err := template.ParseFiles("templates/" + name)
if err != nil {
return nil, err
}
tc.templates[name] = tmpl
return tmpl, nil
}
func (tc *TemplateCache) Render(w http.ResponseWriter, name string, data interface{}) error {
tmpl, err := tc.Get(name)
if err != nil {
return err
}
return tmpl.Execute(w, data)
}
Security Features
Automatic Escaping
// Template automatically escapes based on context
data := map[string]string{
"UserInput": `<script>alert("XSS")</script>`,
}
tmpl.Parse(`<p>{{.UserInput}}</p>`)
// Outputs: <p><script>alert("XSS")</script></p>
Safe HTML
import "html/template"
data := map[string]interface{}{
"SafeHTML": template.HTML("<b>Bold</b>"),
}
tmpl.Parse(`<div>{{.SafeHTML}}</div>`)
// Outputs: <div><b>Bold</b></div>
URL Escaping
data := map[string]string{
"Query": "hello world",
}
tmpl.Parse(`<a href="/search?q={{.Query}}">Search</a>`)
// Outputs: <a href="/search?q=hello%20world">Search</a>
Best Practices
- Use template caching - Parse templates once, reuse many times
- Validate data before rendering - Don’t rely solely on template escaping
- Use typed data structures - Avoid
map[string]interface{}
- Handle errors - Check template execution errors
- Organize templates - Use subdirectories and naming conventions
- Use Must carefully - Only for templates that must parse successfully
- Be careful with HTML/JS/CSS - Use appropriate safe types
- Test templates - Write tests for template rendering
Common Patterns
Base Template Pattern
func loadTemplates() (*template.Template, error) {
base := template.New("base")
base.Funcs(customFuncs)
// Parse all templates
return base.ParseGlob("templates/**/*.html")
}
Layout Inheritance
// base.html
{{define "base"}}
<!DOCTYPE html>
<html>
<head>
<title>{{block "title" .}}Default Title{{end}}</title>
</head>
<body>
{{block "content" .}}Default content{{end}}
</body>
</html>
{{end}}
// page.html
{{template "base" .}}
{{define "title"}}Custom Title{{end}}
{{define "content"}}<p>Custom content</p>{{end}}