DEV Community

Alex Spinov
Alex Spinov

Posted on

Templ Has a Free API — Type-Safe HTML Templates for Go

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
Enter fullscreen mode Exit fullscreen mode
// hello.templ
package main

templ Hello(name string) {
    <div>
        <h1>Hello, { name }!</h1>
        <p>Welcome to Templ.</p>
    </div>
}
Enter fullscreen mode Exit fullscreen mode
templ generate
Enter fullscreen mode Exit fullscreen mode
// main.go
package main

import (
    "net/http"
    "github.com/a-h/templ"
)

func main() {
    http.Handle("/", templ.Handler(Hello("World")))
    http.ListenAndServe(":3000", nil)
}
Enter fullscreen mode Exit fullscreen mode

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>
}
Enter fullscreen mode Exit fullscreen mode

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>
    }
}
Enter fullscreen mode Exit fullscreen mode

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>
}
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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)