A Templ Cheat Sheet for Syntax Components and Helpers
A one page templ cheat sheet: component syntax, control flow, attributes, css and script components, children, fragments, helpers and commands in one program.

This is the reference page for the templ series: syntax, helpers and commands in tables, with one component that uses all of it so you can see the output. The explanations are in the tutorial, the generated code in the first-component article, and the safety rules in the escaping article. It belongs to the web cluster that ends in a production HTTP server. Everything ran on Go 1.25.6 with templ v0.3.1001.
Tested with Go 1.25.6 and templ v0.3.1001, macOS on Apple Silicon (arm64)
One component that uses everything
package main
import (
"fmt"
"strings"
)
type User struct {
Name string
Admin bool
Tags []string
}
css highlight() {
background-color: #fffbdd;
border-left: 4px solid #d4a72c;
}
script confirmDelete(name string) {
return confirm("Delete " + name + "?");
}
templ Card(title string) {
<section class="card">
<h2>{ title }</h2>
{ children... }
</section>
}
templ Badge(label string, attrs templ.Attributes) {
<span class="badge" { attrs... }>{ label }</span>
}
templ Everything(u User, items []string, kind string) {
// a Go comment: not in the output
<!-- an HTML comment: in the output -->
{{ upper := strings.ToUpper(u.Name) }}
@Card("Profile") {
<p>{ upper } has { fmt.Sprint(len(items)) } items</p>
if u.Admin {
@Badge("admin", templ.Attributes{"data-level": "2"})
} else {
@Badge("member", nil)
}
switch kind {
case "a":
<p>kind A</p>
case "b":
<p>kind B</p>
default:
<p>other kind</p>
}
<ul class={ "list", templ.KV("empty", len(items) == 0) }>
for i, it := range items {
<li data-i={ fmt.Sprint(i) } id={ "item-" + it }>{ it }</li>
}
</ul>
<a href={ templ.URL("/users/" + u.Name) } class={ highlight() }>profile</a>
<button type="button" onclick={ confirmDelete(u.Name) }>Delete</button>
<input value={ u.Name } disabled?={ !u.Admin } required?={ true }/>
@templ.Fragment("tags") {
<p>
for _, t := range u.Tags {
<em>{ t }</em>
}
</p>
}
@templ.Raw("<hr/>")
}
}package main
import (
"context"
"fmt"
"strings"
)
func main() {
var sb strings.Builder
err := Everything(User{Name: "ada", Admin: true, Tags: []string{"go", "web"}}, []string{"x", "y"}, "b").Render(context.Background(), &sb)
if err != nil {
panic(err)
}
fmt.Println(strings.ReplaceAll(sb.String(), "><", ">\n<"))
}go get github.com/a-h/[email protected] >/dev/null 2>&1
go get -tool github.com/a-h/templ/cmd/[email protected] >/dev/null 2>&1
go tool templ generate >/dev/null 2>&1
go run .<!-- an HTML comment: in the output -->
<section class="card">
<h2>Profile</h2>
<p>ADA has 2 items</p>
<span class="badge" data-level="2">admin</span> <p>kind B</p> <ul class="list">
<li data-i="0" id="item-x">x</li>
<li data-i="1" id="item-y">y</li>
</ul>
<style type="text/css">.highlight_b768e7b3{background-color:#fffbdd;border-left:4px solid #d4a72c;}</style>
<a href="/users/ada" class="highlight_b768e7b3">profile</a> <script>function __templ_confirmDelete_9e4a(name){return confirm("Delete " + name + "?");
}</script>
<button type="button" onclick="__templ_confirmDelete_9e4a("ada")">Delete</button> <input value="ada" required>
<p>
<em>go</em>
<em>web</em>
</p> <hr/>
</section>The output shows what each construct produces: the Go comment is gone and the HTML comment is kept; the Go statement {{ upper := ... }} computed a value used below; if, switch and for rendered the matching branches; the class list gained empty only when the slice was empty; the css component wrote a <style> block once and a generated class name; the script component wrote a function once and the button calls it with a JSON-quoted argument; disabled? was omitted and required? was written bare; the fragment rendered in place; and templ.Raw passed <hr/> through.
Syntax
| You write | What it does |
|---|---|
package main and Go imports at the top | Ordinary Go, copied to the generated file |
templ Name(a string, b int) { ... } | A component: a Go function returning templ.Component |
<p>{ expr }</p> | Prints a string expression, HTML-escaped for its context |
{{ x := f(y) }} | A Go statement inside a component |
// comment | A Go comment, dropped from the output |
<!-- comment --> | An HTML comment, kept in the output |
if cond { ... } else if c2 { ... } else { ... } | Conditional markup |
switch v { case "a": ... default: ... } | Multiple branches |
for i, x := range xs { ... } | A loop |
@Other(args) | Call another component (on its own line) |
@Layout("t") { ... } | Call with children |
{ children... } | Render the children passed by the caller |
@thing | Render a templ.Component variable or parameter |
Attributes
| You write | Result |
|---|---|
id={ expr } | Attribute with an escaped string value |
class={ "a", "b" } | Class list from several strings |
class={ "row", templ.KV("sold-out", cond) } | Class added only when the condition is true |
class={ highlight() } | Use a css component's generated class |
if cond { aria-current="page" } inside a tag | Conditional attribute |
disabled?={ cond } | Boolean attribute, present only when true |
{ attrs... } | Spread a templ.Attributes map onto the element |
href={ templ.URL(s) } | URL with javascript: and data: blocked |
href={ templ.SafeURL(s) } | URL passed through untouched; only for trusted strings |
onclick={ confirmDelete(name) } | Call a script component with JSON-encoded arguments |
onclick={ templ.JSFuncCall("f", arg) } | Call an existing JavaScript function safely |
Helper functions in the templ package
| Function | Use |
|---|---|
templ.Handler(c, opts...) | Turn a component into an http.Handler |
templ.WithStatus(code) | Handler option: response status |
templ.WithStreaming() | Handler option: write as it renders, no buffering |
templ.WithErrorHandler(f) | Handler option: your own response on a render error |
templ.WithFragments(ids...) | Handler option: render only these fragments |
templ.Fragment(id) | Mark part of a component as an addressable fragment |
templ.Flush() | Component that flushes the writer |
templ.Join(a, b, ...) | Concatenate components |
templ.Raw(html) | Write HTML as given (unsafe for untrusted input) |
templ.JSONScript(id, v) | A script element holding safely encoded JSON |
templ.NewOnceHandle() | Render something once per page (scripts, styles) |
templ.WithNonce(ctx, n) and templ.GetNonce(ctx) | CSP nonce through the context |
templ.ComponentFunc(f) | Make a component from a Go function |
templ.FromGoHTML(t, data) and templ.ToGoHTML(ctx, c) | Bridge to html/template |
Commands
| Command | Use |
|---|---|
go get -tool github.com/a-h/templ/cmd/[email protected] | Pin the generator as a module tool |
go tool templ generate | Generate *_templ.go for the current directory |
go tool templ generate -f file.templ | One file |
go tool templ generate --watch --cmd="go run ." | Regenerate on change and run a command |
go tool templ fmt . | Format .templ files in place |
go tool templ lsp | Start the language server (an editor runs this) |
go tool templ version | Print the version |
Gotchas
A component call must start its own line, or it is read as text (an inline @Count(n) after words printed literally). Expressions in braces must be strings, or return (string, error); format numbers with fmt.Sprint. templ.KV with a false condition can leave an empty class="", while a conditional attribute leaves nothing. Text-only edits hot reload in watch mode, but expression changes restart your command. A failed render after a flush leaves a truncated page with a 200 status. And generated files are build artifacts: regenerate after pulling, and again in CI.
Patterns worth copying
Pass plain values, not requests, into components. Put layouts in components that render { children... }, and named slots in templ.Component parameters. Choose the layout in the handler (full page or fragment). Keep one fragment per region HTMX will re-request. Render to a buffer unless you mean to stream. Test with a string builder and a golden file (testing). Generate in CI before build and test. Treat Raw, SafeURL and any user string in style as review flags.
The full syntax guide is at templ.guide, the generated API on pkg.go.dev, and the series continues with layouts, forms, HTMX and streaming.


