Templ is a Go templating language that generates type-safe Go code from HTML templates. No more html/template string parsing — get compile-time errors for template bugs.
Why Templ?
- Type-safe — template errors caught at compile time, not runtime
- Go code — templates compile to Go functions
- LSP support — autocomplete, go-to-definition in templates
- HTMX-friendly — perfect companion for HTMX + Go stacks
Quick Start
go install github.com/a-h/templ/cmd/templ@latest
// hello.templ
package main
templ Hello(name string) {
<div>
<h1>Hello, { name }!</h1>
<p>Welcome to Templ.</p>
</div>
}
templ generate
// main.go
package main
import (
"net/http"
"github.com/a-h/templ"
)
func main() {
http.Handle("/", templ.Handler(Hello("World")))
http.ListenAndServe(":3000", nil)
}
Components with Props
// components.templ
package components
type User struct {
Name string
Email string
Admin bool
}
templ UserCard(user User) {
<div class="card">
<h3>{ user.Name }</h3>
<p>{ user.Email }</p>
if user.Admin {
<span class="badge">Admin</span>
}
</div>
}
templ UserList(users []User) {
<div class="user-list">
for _, user := range users {
@UserCard(user)
}
</div>
}
Layouts
templ Layout(title string) {
<!DOCTYPE html>
<html>
<head>
<title>{ title }</title>
<link rel="stylesheet" href="/static/styles.css" />
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/users">Users</a>
</nav>
<main>
{ children... }
</main>
</body>
</html>
}
templ HomePage() {
@Layout("Home") {
<h1>Welcome</h1>
<p>This is the home page.</p>
}
}
HTMX Integration
templ TodoItem(todo Todo) {
<li id={ fmt.Sprintf("todo-%d", todo.ID) }>
<span>{ todo.Title }</span>
<button
hx-delete={ fmt.Sprintf("/todos/%d", todo.ID) }
hx-target={ fmt.Sprintf("#todo-%d", todo.ID) }
hx-swap="outerHTML"
>
Delete
</button>
</li>
}
templ TodoList(todos []Todo) {
<ul id="todo-list">
for _, todo := range todos {
@TodoItem(todo)
}
</ul>
<form hx-post="/todos" hx-target="#todo-list" hx-swap="beforeend">
<input type="text" name="title" />
<button type="submit">Add</button>
</form>
}
With Chi Router
func main() {
r := chi.NewRouter()
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
HomePage().Render(r.Context(), w)
})
r.Get("/users", func(w http.ResponseWriter, r *http.Request) {
users := getUsers()
UserList(users).Render(r.Context(), w)
})
http.ListenAndServe(":3000", r)
}
Building Go web apps that need data? Check out my Apify actors for web scraping, or email spinov001@gmail.com for custom Go development.
Templ + HTMX or React SPA — which stack do you prefer? Comment below!
Top comments (0)