Documentation Index
Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
Use this file to discover all available pages before exploring further.
This guide covers everything you need to know about writing templates in Mizu’s view system. We’ll explore Go’s template syntax, how data flows through your templates, and common patterns.
Template Basics
Mizu uses Go’s standard html/template package. Templates are HTML files with special markers ({{ and }}) for dynamic content.
<!-- views/pages/home.html -->
<h1>Hello, {{.Data.Name}}!</h1>
<p>Welcome to our site.</p>
Accessing Data
In Mizu templates, your data is wrapped in a structure. Access your data using .Data:
// In your handler:
view.Render(c, "home", view.Data{
"Title": "Welcome",
"User": user,
"Items": items,
})
<!-- In your template: -->
<h1>{{.Data.Title}}</h1>
<p>Hello, {{.Data.User.Name}}</p>
The Template Data Structure
Templates receive this structure:
| Field | Description | Available In |
|---|
.Page.Name | Template name (e.g., “home”) | Pages and Layouts |
.Page.Layout | Layout name (e.g., “default”) | Pages and Layouts |
.Data | Your data from the handler | Pages and Layouts |
.Content | Rendered page content | Layouts only |
Go Template Syntax
Outputting Values
Use .Data. to access your data:
<!-- Simple value -->
<h1>{{.Data.Title}}</h1>
<!-- Nested struct field -->
<p>Author: {{.Data.Post.Author.Name}}</p>
<!-- Map key -->
<p>Setting: {{.Data.Settings.theme}}</p>
Conditions
Use if to show content conditionally:
{{if .Data.LoggedIn}}
<p>Welcome back!</p>
{{else}}
<p>Please log in.</p>
{{end}}
{{if .Data.Error}}
<div class="error">{{.Data.Error}}</div>
{{end}}
Comparison Operators
Go templates provide comparison functions:
| Function | Description | Example |
|---|
eq | Equal | {{if eq .Data.Status "active"}} |
ne | Not equal | {{if ne .Data.Count 0}} |
lt | Less than | {{if lt .Data.Price 100}} |
le | Less than or equal | {{if le .Data.Stock 5}} |
gt | Greater than | {{if gt .Data.Price 100}} |
ge | Greater than or equal | {{if ge .Data.Age 18}} |
{{if eq .Data.Status "active"}}Active{{end}}
{{if ne .Data.Count 0}}Has items{{end}}
{{if gt .Data.Price 100}}Premium{{end}}
{{if le .Data.Stock 5}}Low stock{{end}}
Boolean Logic
Combine conditions with and, or, and not:
{{if and .Data.LoggedIn .Data.IsAdmin}}
Admin controls
{{end}}
{{if or .Data.Error .Data.Warning}}
<div class="alert">Check messages</div>
{{end}}
{{if not .Data.Disabled}}
<button>Click me</button>
{{end}}
Loops
Use range to iterate over slices, arrays, or maps:
<!-- Loop over slice -->
<ul>
{{range .Data.Items}}
<li>{{.}}</li>
{{end}}
</ul>
<!-- With index -->
<ul>
{{range $index, $item := .Data.Items}}
<li>{{$index}}: {{$item}}</li>
{{end}}
</ul>
<!-- Empty case -->
{{range .Data.Items}}
<li>{{.}}</li>
{{else}}
<li>No items found.</li>
{{end}}
Looping Over Structs
When ranging over a slice of structs:
{{range .Data.Users}}
<!-- Inside range, . is the current user -->
<div class="user">
<h3>{{.Name}}</h3>
<p>{{.Email}}</p>
</div>
{{end}}
Looping Over Maps
{{range $key, $value := .Data.Settings}}
<p>{{$key}}: {{$value}}</p>
{{end}}
Variables
Assign values to variables with :=:
{{$name := .Data.User.Name}}
<p>Hello, {{$name}}!</p>
{{$fullName := printf "%s %s" .Data.FirstName .Data.LastName}}
<p>{{$fullName}}</p>
The Root Context ($)
Inside a range loop, . refers to the current item. Use $ to access the root context:
{{range .Data.Users}}
<p>{{.Name}}</p>
{{if $.Data.ShowEmail}}
<p>{{.Email}}</p>
{{end}}
{{end}}
$ always refers to the original data passed to the template.
Pipelines
Chain operations with |:
<!-- Apply function to value -->
<p>{{.Data.Name | upper}}</p>
<!-- Chain multiple functions -->
<p>{{.Data.Title | lower | trim}}</p>
Template comments are stripped from output:
{{/* This comment won't appear in the HTML */}}
<p>Visible content</p>
Whitespace Control
Use - to trim whitespace:
{{- .Data.Name -}} <!-- Trims whitespace before and after -->
{{- .Data.Name}} <!-- Trims whitespace before -->
{{.Data.Name -}} <!-- Trims whitespace after -->
This is useful for keeping your HTML clean:
<ul>
{{- range .Data.Items}}
<li>{{.}}</li>
{{- end}}
</ul>
Layouts and Pages
Layout Structure
Layouts use {{.Content}} to include the rendered page:
<!-- views/layouts/default.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.Data.Title}}</title>
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main>
{{.Content}}
</main>
<footer>
Copyright 2024
</footer>
</body>
</html>
Page Structure
Pages are simple - just write HTML with template expressions:
<!-- views/pages/home.html -->
<h1>Welcome, {{.Data.Name}}!</h1>
<p>This is the home page.</p>
The page content is rendered first, then inserted into the layout’s {{.Content}}.
Common Patterns
Conditional Classes
<div class="card {{if .Data.Featured}}card-featured{{end}}">
...
</div>
<!-- Multiple conditional classes -->
<button class="btn {{if .Data.Primary}}btn-primary{{else}}btn-secondary{{end}} {{if .Data.Large}}btn-lg{{end}}">
{{.Data.Label}}
</button>
Dynamic Attributes
<input
type="text"
name="{{.Data.Name}}"
value="{{.Data.Value}}"
{{if .Data.Required}}required{{end}}
{{if .Data.Disabled}}disabled{{end}}
>
Safe HTML Output
By default, Go templates escape HTML to prevent XSS attacks:
<!-- Escaped (safe) - shows HTML as text -->
<div>{{.Data.Content}}</div>
<!-- If .Data.Content is "<b>bold</b>", outputs: <b>bold</b> -->
To output raw HTML (use only with trusted content!):
import "html/template"
view.Data{
"Content": template.HTML("<b>bold</b>"),
}
<div>{{.Data.Content}}</div>
<!-- Now outputs: <b>bold</b> -->
Empty/Nil Checks
<!-- Check for empty string -->
{{if .Data.Description}}
<p>{{.Data.Description}}</p>
{{end}}
<!-- Check slice length -->
{{if .Data.Items}}
<ul>
{{range .Data.Items}}
<li>{{.}}</li>
{{end}}
</ul>
{{else}}
<p>No items found.</p>
{{end}}
Building Links
<a href="/users/{{.Data.User.ID}}">View Profile</a>
<a href="/posts?page={{.Data.Page}}">Next Page</a>
Use custom functions or printf:
<p>Price: ${{printf "%.2f" .Data.Price}}</p>
<p>Count: {{printf "%d" .Data.Count}}</p>
For dates, either format in Go or use a custom function:
view.Data{
"FormattedDate": time.Now().Format("January 2, 2006"),
}
Or add a custom function:
view.Config{
Funcs: template.FuncMap{
"formatDate": func(t time.Time) string {
return t.Format("Jan 2, 2006")
},
},
}
<p>Published: {{formatDate .Data.PublishedAt}}</p>
Debugging Templates
Print Values
Use printf with %#v to see the Go syntax representation:
<pre>{{printf "%#v" .Data}}</pre>
Check Types
<p>Type: {{printf "%T" .Data.Items}}</p>
Enable Development Mode
For detailed error messages with line numbers:
view.New(view.Config{
Development: true,
})
Best Practices
1. Keep Templates Simple
Put complex logic in Go handlers, not templates:
// Good: logic in handler
isVIP := user.TotalPurchases > 1000 && user.MemberSince.Before(cutoff)
view.Data{"IsVIP": isVIP}
<!-- Template just uses the result -->
{{if .Data.IsVIP}}VIP Member{{end}}
2. Use Meaningful Variable Names
{{range $user := .Data.Users}}
<p>{{$user.Name}}</p>
{{end}}
3. Document Expected Data
Add comments at the top of templates:
{{/*
Expected data:
- Title: string
- User: {Name, Email}
- Posts: []Post
*/}}
Always let Go’s default escaping protect against XSS. Only use template.HTML for content you trust completely.
5. Use Whitespace Trimming
Keep generated HTML clean:
<ul>
{{- range .Data.Items}}
<li>{{.}}</li>
{{- end}}
</ul>