Components and Syntax
A .templ file is ordinary Go plus component declarations. Outside components you write normal Go (package, imports, helper functions). Inside, markup mixes with expressions, and templ generate turns the whole thing into a Go file the compiler checks.
Defining a component
package views
import "fmt"
templ greeting(name string) {
<p>Hello, { fmt.Sprintf("%s!", name) }</p>
}
After templ generate, this becomes a Go function:
func greeting(name string) templ.Component
Every component satisfies one interface (runtime.go):
type Component interface {
Render(ctx context.Context, w io.Writer) error
}
templ.ComponentFunc adapts an ordinary closure into a Component, which is handy for small inline pieces.
Expressions
{ expr } inserts a Go expression into text content or attribute values. Output is HTML-escaped automatically.
templ userCard(name string, admin bool) {
<div class="card">
<h2>{ name }</h2>
<span data-admin={ fmt.Sprint(admin) }></span>
</div>
}
Gotcha: Escaping applies to every expression. To emit trusted HTML you must opt out explicitly with
templ.Raw- see Security and Scripts.
Control flow is Go
There is no template mini-language. Use if, switch, and for directly:
templ items(items []string) {
if len(items) == 0 {
<p>Nothing here.</p>
} else {
<ul>
for _, item := range items {
<li>{ item }</li>
}
</ul>
}
}
Composition and children
Call another component with @. Components can take other components as parameters, which is how layouts work:
templ layout(contents templ.Component) {
<html>
<body>
@contents
</body>
</html>
}
templ paragraph(text string) {
<p>{ text }</p>
}
templ page() {
@layout(paragraph("hello"))
}
{ children... } is the alternative: it renders whatever children the caller passed in.
templ.Join, KV, Once, Raw
| Helper | Purpose | Where |
|---|---|---|
templ.Join(components...) | Render several components as one | join.go |
templ.KV(name string, cond bool) | Conditional entry in a class={ ... } list | runtime.go |
templ.Once() | Render-once semantics for content that would otherwise repeat | once.go |
templ.Raw(html) | Emit unescaped HTML (escape hatch) | runtime.go |
templ badge(label string, active bool) {
<span class={ "badge", templ.KV("badge-active", active) }>{ label }</span>
}
Tip: Reach for
templ.Joinwhen a component needs to return several sibling elements - it composes children without introducing a wrapper element.