← Voltar para o blog

Templ em Go: Templates HTML Type-Safe para Web

Aprenda a usar Templ em Go para criar componentes HTML type-safe com autocompletar, checagem em tempo de compilação e integração nativa com htmx.

O pacote html/template da standard library do Go funciona, mas tem limitações que incomodam em projetos maiores: erros de template só aparecem em runtime, não há autocompletar no editor, e a composição de componentes é manual e frágil. Qualquer typo no nome de um campo ou uma struct errada só explode quando o usuário acessa a página.

O Templ resolve esses problemas com uma abordagem diferente: você escreve templates em arquivos .templ com uma sintaxe próxima do Go, e o compilador gera código Go type-safe. Erros aparecem no build, não em produção. O editor mostra autocompletar, refatoração funciona, e componentes se compõem como funções normais.

Se você já trabalha com Go para aplicações web ou está montando interfaces server-side, Templ é a ferramenta que faltava no ecossistema.

O problema com html/template

Considere um template tradicional em Go:

// handler.go
func handler(w http.ResponseWriter, r *http.Request) {
    tmpl := template.Must(template.ParseFiles("templates/home.html"))
    data := PageData{
        Title: "Minha Página",
        Items: []string{"Go", "Templ", "htmx"},
    }
    tmpl.Execute(w, data)
}
<!-- templates/home.html -->
<h1>{{ .Title }}</h1>
<ul>
{{ range .Items }}
    <li>{{ . }}</li>
{{ end }}
</ul>

Os problemas aparecem rápido:

  • Se você renomear Title para Name na struct, o template compila sem erro e falha silenciosamente em runtime
  • Sem autocompletar – o editor não sabe quais campos a struct tem dentro do template
  • Composição de componentes exige template.Block, define e template aninhados, que ficam verbosos
  • Sem checagem de tipos – passar um int onde espera string só dá erro quando o template renderiza

Instalando e configurando o Templ

Instale o CLI do Templ:

go install github.com/a-h/templ/cmd/templ@latest

Para desenvolvimento, o Templ tem um modo watch que regenera código automaticamente:

templ generate --watch

No VS Code, instale a extensão templ-vscode para syntax highlighting e autocompletar dentro de arquivos .templ.

Seu primeiro componente Templ

Crie um arquivo components/hello.templ:

package components

// Hello renderiza uma saudação personalizada.
// O parâmetro name é verificado em tempo de compilação.
templ Hello(name string) {
    <div class="greeting">
        <h1>Olá, { name }!</h1>
        <p>Bem-vindo ao Golang Brasil.</p>
    </div>
}

Rode templ generate para gerar o código Go correspondente. O resultado é um arquivo hello_templ.go com uma função tipada que implementa templ.Component:

// Gerado automaticamente pelo templ -- não edite
func Hello(name string) templ.Component {
    return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
        // ... código de renderização
    })
}

Agora use o componente no seu handler:

package main

import (
    "net/http"
    "meuapp/components"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // Type-safe: se você passar um int, o compilador avisa
        components.Hello("Maria").Render(r.Context(), w)
    })
    http.ListenAndServe(":8080", nil)
}

Se você tentar components.Hello(42), o compilador Go rejeita na hora. Sem surpresas em produção.

Composição de componentes

Templ brilha na composição. Você pode aninhar componentes como funções:

package components

// Layout é o template base com header, conteúdo e footer.
templ Layout(title string) {
    <!DOCTYPE html>
    <html lang="pt-br">
    <head>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <title>{ title } | Meu App</title>
        <link rel="stylesheet" href="/static/style.css"/>
    </head>
    <body>
        @Header()
        <main class="container">
            { children... }
        </main>
        @Footer()
    </body>
    </html>
}

// Header renderiza a navegação principal.
templ Header() {
    <header class="site-header">
        <nav>
            <a href="/">Início</a>
            <a href="/sobre">Sobre</a>
            <a href="/contato">Contato</a>
        </nav>
    </header>
}

// Footer renderiza o rodapé do site.
templ Footer() {
    <footer class="site-footer">
        <p>© 2026 Meu App. Feito com Go e Templ.</p>
    </footer>
}

E usar o layout em uma página:

package pages

import "meuapp/components"

// HomePage renderiza a página inicial com o layout padrão.
templ HomePage(userName string, posts []Post) {
    @components.Layout("Início") {
        <section class="hero">
            <h1>Olá, { userName }</h1>
        </section>
        <section class="posts">
            for _, post := range posts {
                @PostCard(post)
            }
        </section>
    }
}

// PostCard renderiza um card de post individual.
templ PostCard(post Post) {
    <article class="card">
        <h2>{ post.Title }</h2>
        <p>{ post.Summary }</p>
        <a href={ templ.SafeURL("/posts/" + post.Slug) }>Ler mais</a>
    </article>
}

A composição @components.Layout("Início") { ... } funciona como children em React, mas com zero overhead de JavaScript.

Integração com htmx

Templ e htmx formam uma dupla poderosa para aplicações web interativas sem JavaScript pesado. O htmx faz requisições AJAX e atualiza o DOM parcialmente, enquanto Templ gera os fragmentos HTML no servidor.

package components

// SearchBar renderiza uma barra de busca com htmx.
templ SearchBar() {
    <div class="search">
        <input
            type="search"
            name="q"
            placeholder="Buscar..."
            hx-get="/api/search"
            hx-trigger="keyup changed delay:300ms"
            hx-target="#results"
        />
        <div id="results"></div>
    </div>
}

// SearchResults renderiza os resultados da busca.
// Retornado como fragmento HTML para o htmx.
templ SearchResults(results []SearchResult) {
    if len(results) == 0 {
        <p class="empty">Nenhum resultado encontrado.</p>
    } else {
        <ul class="results-list">
            for _, r := range results {
                <li>
                    <a href={ templ.SafeURL(r.URL) }>
                        <strong>{ r.Title }</strong>
                        <span>{ r.Description }</span>
                    </a>
                </li>
            }
        </ul>
    }
}

O handler no servidor retorna apenas o fragmento HTML:

func searchHandler(w http.ResponseWriter, r *http.Request) {
    query := r.URL.Query().Get("q")
    results := searchService.Search(query)

    // Retorna apenas o fragmento, sem o layout completo
    components.SearchResults(results).Render(r.Context(), w)
}

O htmx recebe o HTML e substitui o conteúdo do #results automaticamente. Sem JSON, sem parsing no cliente, sem framework de frontend.

Condicionais e loops nativos

Templ usa a sintaxe do Go para controle de fluxo, sem inventar uma linguagem de template nova:

package components

// UserProfile renderiza o perfil com badges condicionais.
templ UserProfile(user User) {
    <div class="profile">
        <h2>{ user.Name }</h2>

        if user.IsAdmin {
            <span class="badge admin">Administrador</span>
        } else if user.IsModerator {
            <span class="badge mod">Moderador</span>
        }

        if len(user.Skills) > 0 {
            <div class="skills">
                <h3>Habilidades</h3>
                <ul>
                    for _, skill := range user.Skills {
                        <li>{ skill }</li>
                    }
                </ul>
            </div>
        }

        switch user.Level {
            case "junior":
                <span class="level">Júnior</span>
            case "pleno":
                <span class="level">Pleno</span>
            case "senior":
                <span class="level">Sênior</span>
        }
    </div>
}

if, for, switch – tudo funciona como no Go. Sem aprender {{ if }}, {{ range }}, {{ with }} e suas peculiaridades.

CSS e JavaScript inline

Templ suporta CSS e JavaScript com escopo por componente:

package components

// StyledButton renderiza um botão com estilo isolado.
templ StyledButton(label string) {
    <style>
        .btn-primary {
            background-color: #00ADD8;
            color: white;
            padding: 12px 24px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 1rem;
            transition: background-color 0.2s ease;
        }
        .btn-primary:hover {
            background-color: #0097B9;
        }
    </style>
    <button class="btn-primary" onclick="handleClick(this)">
        { label }
    </button>
    <script>
        function handleClick(el) {
            el.textContent = "Clicado!";
        }
    </script>
}

O CSS gerado é emitido uma única vez por componente, mesmo que você use o componente várias vezes na página.

Comparação com alternativas

Recursohtml/templateTemplReact/Next.js
Type-safetyNãoSimSim (TSX)
Checagem no buildNãoSimSim
AutocompletarNãoSimSim
Tamanho do bundle JS0 KB0 KB100+ KB
ComposiçãoVerbosaNaturalNatural
Curva de aprendizadoBaixaBaixaAlta
Servidor necessárioGoGoNode.js

Templ ocupa o espaço ideal: type-safety de frameworks modernos com a simplicidade e performance do Go server-side. Sem Node.js, sem build tools JavaScript, sem hydration.

Organizando um projeto com Templ

Uma estrutura recomendada para projetos Go com Templ:

meuapp/
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── handlers/
│   │   └── home.go
│   └── services/
│       └── user.go
├── components/
│   ├── layout.templ
│   ├── header.templ
│   ├── footer.templ
│   └── cards.templ
├── pages/
│   ├── home.templ
│   ├── about.templ
│   └── contact.templ
├── static/
│   └── style.css
├── go.mod
└── Makefile

No Makefile, adicione um target para gerar código:

generate:
	templ generate

dev:
	templ generate --watch &
	go run ./cmd/server

build:
	templ generate
	go build -o bin/server ./cmd/server

Quando usar Templ

Templ faz sentido quando:

  • Você quer aplicações web em Go sem depender de frameworks JavaScript
  • Erros de template em produção já causaram problemas reais no seu time
  • Você precisa de composição de componentes sem a verbosidade de html/template
  • Sua stack já usa htmx ou quer server-side rendering puro

Se você está construindo uma SPA complexa com estado pesado no cliente, React ou Vue continuam sendo a melhor escolha. Mas para sites, dashboards, painéis administrativos e aplicações CRUD, Templ com Go elimina uma camada inteira de complexidade.

Para quem vem de outras linguagens, vale comparar como Kotlin aborda templating web e como Rust lida com frameworks web server-side. Go com Templ se posiciona entre os dois em simplicidade e performance.

Explore também nosso guia de Go para backend e o tutorial sobre APIs REST em Go para entender como Templ se integra em uma aplicação completa.