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]]

Next Steps