Template System
Overview
Weber uses Go's built-in template engine for server-side rendering. Templates are HTML files with embedded Go template syntax that allow you to dynamically generate content.
Template Basics
Template Syntax
Go templates use [[ ]] delimiters for dynamic content:
<!DOCTYPE html>
<html>
<head>
<title>[[.title]]</title>
</head>
<body>
<h1>[[.heading]]</h1>
<p>[[.content]]</p>
</body>
</html>
Rendering Templates
From a route handler:
func Handler(ctx *app.Context) {
ctx.Render("template.html", map[string]interface{}{
"title": "My Page",
"heading": "Welcome",
"content": "This is the content",
})
}
Template Functions
Weber provides custom template functions for common operations:
String Functions
[[/* Convert to uppercase */]]
[[upper .name]]
[[/* Convert to lowercase */]]
[[lower .name]]
[[/* Truncate string */]]
[[truncate .text 100]]
[[/* Format date */]]
[[formatDate .timestamp "2006-01-02"]]
[[/* URL encode */]]
[[urlEncode .query]]
Array Functions
[[/* Get array length */]]
[[len .items]]
[[/* Join array elements */]]
[[join .tags ", "]]
[[/* Check if array contains value */]]
[[if contains .tags "news"]]
<span>News Article</span>
[[end]]
Comparison Functions
[[/* Equal */]]
[[if eq .status "active"]]Active[[end]]
[[/* Not equal */]]
[[if ne .count 0]]Count: [[.count]][[end]]
[[/* Greater than */]]
[[if gt .score 100]]High Score![[end]]
[[/* Less than or equal */]]
[[if le .age 18]]Minor[[end]]
Control Structures
Conditionals
[[if .user]]
<p>Welcome, [[.user.name]]!</p>
[[else]]
<p>Please log in.</p>
[[end]]
[[/* Multiple conditions */]]
[[if .isAdmin]]
<p>Admin Panel</p>
[[else if .isModerator]]
<p>Moderator Panel</p>
[[else]]
<p>User Panel</p>
[[end]]
Loops
[[/* Range over array */]]
<ul>
[[range .items]]
<li>[[.]]</li>
[[end]]
</ul>
[[/* Range with index */]]
[[range $index, $item := .items]]
<div>[[$index]]: [[$item]]</div>
[[end]]
[[/* Range over map */]]
[[range $key, $value := .config]]
<p>[[$key]]: [[$value]]</p>
[[end]]
[[/* Empty check */]]
[[range .items]]
<li>[[.]]</li>
[[else]]
<p>No items found</p>
[[end]]
With Blocks
[[with .user]]
<p>Name: [[.name]]</p>
<p>Email: [[.email]]</p>
[[else]]
<p>No user data</p>
[[end]]
Template Composition
Defining Templates
Create reusable template blocks:
[[/* base.html */]]
[[define "base"]]
<!DOCTYPE html>
<html>
<head>
<title>[[.title]]</title>
[[template "styles" .]]
</head>
<body>
[[template "header" .]]
<main>
[[template "content" .]]
</main>
[[template "footer" .]]
[[template "scripts" .]]
</body>
</html>
[[end]]
Using Templates
[[/* page.html */]]
[[define "content"]]
<h1>[[.heading]]</h1>
<p>[[.text]]</p>
[[end]]
[[define "styles"]]
<link rel="stylesheet" href="/static/css/page.css">
[[end]]
[[define "scripts"]]
<script src="/static/js/page.js"></script>
[[end]]
[[template "base" .]]
Partial Templates
Create reusable components:
Card Component
[[/* partials/card.html */]]
[[define "card"]]
<div class="card">
[[if .image]]
<img src="[[.image]]" alt="[[.title]]">
[[end]]
<h3>[[.title]]</h3>
<p>[[.description]]</p>
[[if .link]]
<a href="[[.link]]">Read More</a>
[[end]]
</div>
[[end]]
Using Partials
[[range .articles]]
[[template "card" .]]
[[end]]
Data Context
Accessing Data
[[/* Current context */]]
[[.fieldName]]
[[/* Nested fields */]]
[[.user.profile.name]]
[[/* Variables */]]
[[$title := .title]]
<h1>[[$title]]</h1>
[[/* Pipeline */]]
[[.text | upper | truncate 50]]
Passing Context
[[/* Pass entire context */]]
[[template "partial" .]]
[[/* Pass specific data */]]
[[template "partial" .user]]
[[/* Pass custom data */]]
[[template "partial" (dict "name" .name "age" .age)]]
Asset Management
Static Assets
<!-- CSS -->
<link rel="stylesheet" href="/static/css/style.css">
<!-- JavaScript -->
<script src="/static/js/main.js"></script>
<!-- Images -->
<img src="/static/img/logo.png" alt="Logo">
Cache Busting
[[/* With hash */]]
<link rel="stylesheet" href="/static/css/style.[[.hash]].css">
<script src="/static/js/main.[[.hash]].js"></script>
Preact Integration
Integrate Preact components into templates:
Component Placeholder
<!-- Define mount point -->
<div id="app" data-props='[[.propsJSON]]'></div>
<!-- Load component bundle -->
<script src="/static/js/component.bundle.js"></script>
Passing Data to Components
func Handler(ctx *app.Context) {
props := map[string]interface{}{
"user": user,
"items": items,
}
propsJSON, _ := json.Marshal(props)
ctx.Render("page.html", map[string]interface{}{
"propsJSON": string(propsJSON),
})
}
SEO and Meta Tags
<head>
<title>[[.meta.title]]</title>
<meta name="description" content="[[.meta.description]]">
<meta name="keywords" content="[[join .meta.keywords ", "]]">
<!-- Open Graph -->
<meta property="og:title" content="[[.meta.title]]">
<meta property="og:description" content="[[.meta.description]]">
<meta property="og:image" content="[[.meta.image]]">
<meta property="og:url" content="[[.meta.url]]">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="[[.meta.title]]">
<meta name="twitter:description" content="[[.meta.description]]">
<meta name="twitter:image" content="[[.meta.image]]">
</head>
Error Handling
Safe Rendering
[[/* Default value if field is empty */]]
[[if .title]][[.title]][[else]]Untitled[[end]]
[[/* Use with */]]
[[with .user]]
[[.name]]
[[else]]
Guest
[[end]]
[[/* Check before access */]]
[[if and .article .article.author]]
By [[.article.author.name]]
[[end]]
Security
Escaping
HTML is automatically escaped by default:
[[/* Automatically escaped */]]
[[.userInput]]
[[/* Unescaped (use with caution!) */]]
[[.trustedHTML | safe]]
XSS Prevention
[[/* Safe attribute values */]]
<div class="[[.className]]">
[[/* Safe URLs */]]
<a href="[[.url]]">Link</a>
[[/* JavaScript context (be careful!) */]]
<script>
var data = [[.jsonData | toJSON]];
</script>
Performance Tips
- Template Caching - Templates are cached in production
- Minimize Logic - Keep complex logic in handlers, not templates
- Precompute Data - Format data in handlers before passing to templates
- Use Partials Wisely - Balance reusability with overhead
- Optimize Loops - Avoid nested loops when possible
Best Practices
- Keep templates simple and focused
- Use meaningful variable names
- Comment complex template logic
- Create reusable partials for common UI elements
- Validate data in handlers before rendering
- Use template composition for consistent layouts
- Test templates with various data scenarios
Example: Complete Page Template
[[define "news-detail"]]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>[[.article.title]] - Weber News</title>
<meta name="description" content="[[.article.excerpt]]">
<link rel="stylesheet" href="/static/css/news.css">
</head>
<body>
[[template "header" .]]
<article class="news-article">
[[if .article.image]]
<img src="[[.article.image]]" alt="[[.article.title]]">
[[end]]
<h1>[[.article.title]]</h1>
<div class="meta">
<span class="author">By [[.article.author]]</span>
<span class="date">[[formatDate .article.date "Jan 2, 2006"]]</span>
<span class="category">[[.article.category]]</span>
</div>
<div class="content">
[[.article.content | safe]]
</div>
[[if .article.tags]]
<div class="tags">
[[range .article.tags]]
<a href="/news?tag=[[.]]" class="tag">[[.]]</a>
[[end]]
</div>
[[end]]
</article>
[[if .relatedArticles]]
<section class="related">
<h2>Related Articles</h2>
<div class="grid">
[[range .relatedArticles]]
[[template "article-card" .]]
[[end]]
</div>
</section>
[[end]]
[[template "footer" .]]
<script src="/static/js/news.bundle.js"></script>
</body>
</html>
[[end]]