Basics

Templates

Type-safe HTML with Templ.

HAMR uses Templ for views. Templ compiles .templ files into Go code, giving you compile-time errors, full IDE autocomplete, and zero runtime template parsing.

A basic component

internal/web/handler/books/list.templ
package books

import (
    "github.com/labstack/echo/v4"
    "github.com/yourname/myapp/internal/web/components"
    "github.com/yourname/myapp/internal/repo"
)

templ listPage(c echo.Context, books []repo.Book) {
    @components.Layout(c, "Books") {
        <main class="max-w-4xl mx-auto px-6 py-12">
            <h1 class="text-3xl font-bold mb-6">Books</h1>
            <ul class="space-y-2">
                for _, book := range books {
                    <li class="p-4 rounded border">
                        <a href={ templ.SafeURL("/books/" + book.ID) }>
                            { book.Title }
                        </a>
                    </li>
                }
            </ul>
        </main>
    }
}

Templ components are just Go functions. They take typed parameters, can call each other, and pass children blocks via children syntax.

Layouts

Every HAMR project ships with a components/layout.templ base layout. Pages call @components.Layout(c, title) and pass their content as a child block.

internal/web/components/layout.templ
package components

import "github.com/labstack/echo/v4"

templ Layout(c echo.Context, title string) {
    <!DOCTYPE html>
    <html lang="en" class="dark">
    <head>
        <meta charset="UTF-8"/>
        <title>{ title } — My App</title>
        <link rel="stylesheet" href={ StaticURL("css/output.css") }/>
    </head>
    <body class="bg-slate-900 text-slate-200">
        { children... }
        <script src={ StaticURL("js/htmx.min.js") }></script>
        <script src={ StaticURL("js/alpine.min.js") } defer></script>
    </body>
    </html>
}

Static URL helper

Use components.StaticURL(path) to reference static assets. It prepends STATIC_BASE_URL (handy for CDN hosting) and appends a cache-busting version query string set from the build's git commit.

usage
<link rel="stylesheet" href={ components.StaticURL("css/output.css") }/>
<script src={ components.StaticURL("js/htmx.min.js") }></script>

Rendering from a handler

Render Templ components with respond.HTML. It handles content negotiation: if the request is HTML (browser or HTMX), it renders the component; if the client wants JSON, it returns an error.

handler.go
func (h *handler) Index(c echo.Context) error {
    return respond.HTML(c, http.StatusOK, listPage(c, books))
}

Generating Go code

Templ files are compiled to Go via templ generate. The HAMR dev server runs this automatically on file change. You can also run it manually:

terminal
templ generate
Lint your templates
Run make templint to catch common Templ mistakes — silent failures, accessibility issues, control-flow gotchas. The HAMR CLI ships with a custom Templ linter.