Generated Code
templ generate writes one *_templ.go per .templ file. Reading what is inside explains templ’s runtime behavior: why it is fast, why the LSP works, and why you must regenerate.
What is in the file
A component becomes a function returning a templruntime.GeneratedTemplate (simplified from real generated output):
func hello(name string) templ.Component {
return templruntime.GeneratedTemplate(func(
in templruntime.GeneratedComponentInput,
) (err error) {
buf, isNew := templruntime.GetBuffer(in.Writer) // pooled buffer
if !isNew {
defer func() { err = templruntime.ReleaseBuffer(buf) }()
}
// static HTML: written as a constant at a fixed position index
err = templruntime.WriteString(buf, 1, "<h1>Hello, ")
if err != nil {
return err
}
// { name } here: escaped, then written to the same buffer
_ = name
return nil
})
}
The mechanisms that matter, all visible in generated files:
- Buffer pooling -
templruntime.GetBuffer/ReleaseBufferrecycle render buffers across requests, avoiding per-request allocations - Static HTML as constants - markup literals become string constants written via
templruntime.WriteString(buffer, positionIndex, "constant") - Position indexes - the index maps output back to
.templsource lines; this is what powers LSP diagnostics and source maps - Version stamp - a templ version string is baked into each generated file unless you pass
-include-version=false, so stale files are detectable
What this buys you
- Compile-time checking - components have typed parameters; wrong arguments fail the build
- Zero parse at runtime - no template registry, nothing to parse on boot
- Render errors are Go errors -
Renderreturns anerroryou handle like any other - No reflection on the render path - output is plain write calls
The cost
Gotcha: Generated files must be regenerated when
.templfiles change. A stale*_templ.gocompiles fine and silently serves old markup - this is the most common templ surprise. Runtempl generate --watchin development.
Static generation
Render accepts any writer, so build-time generation is a loop over routes into files:
for route, component := range routes {
f, _ := os.Create("public/" + route)
component.Render(ctx, f)
f.Close()
}
Pair with go:embed to ship a fully static site inside one binary; handlers then only serve dynamic endpoints. templ’s docs cover this mode under static rendering.
Versus html/template
| Aspect | templ | html/template |
|---|---|---|
| Parse step | None - output is compiled code | Parse templates at runtime |
| Indirection | Direct function calls | template.FuncMap lookups |
| Type errors | Compile time | Runtime |
| Updating markup | Regenerate and rebuild | Reload template files |
The trade in one line: templ moves template work from request time to build time, and charges you a regeneration step for it. See also Rendering Model.