Templ
With ordinary templates, a mistake is invisible until somebody loads the page: rename a struct field and the template keeps referring to the old name, printing nothing where the value should be. Nothing fails, nothing is logged, and the page is just quietly wrong.
Templ fixes that by compiling your templates into Go
code. A component becomes a Go function taking typed arguments, so a renamed
field is a compile error in the same build as the rest of your application.
The cost is a generation step: after editing a .templ file you run
templ generate, or let lemmego dev watch and do it for you.
Setup
If you chose Templ when you ran lemmego new, everything here is already in
place — the files, the configuration and the watcher. There is nothing for you
to set up.
Rendering a Templ Component
import "github.com/lemmego/templ"
func MyHandler(c app.Context) error {
return c.Templ(components.Hello("World"))
}Or using the package-level helper:
return templ.Respond(c, components.Hello("World"))TemplResponse
The TemplResponse type implements the Renderer interface, enabling it to be used with c.Render():
response := templ.New(c, components.Page(data))
return c.Render(response)How It Works
templ.New(ctx, component)creates aTemplResponsewrapping atempl.Component- On render, it sets
Content-Type: text/html, writes the HTTP status code, then callscomponent.Render(ctx.RequestContext(), w)
Template Sources
Templ source files are located in templates/:
templates/
├── base-layout.templ # Base layout component
├── index.templ # Index page component
├── method.templ # Method override helper
└── csrf.templ # CSRF token helperThe generated _templ.go files (compiled from .templ) should not be edited directly.
Development Workflow
During development, run Templ in watch mode:
lemmego devThis starts templ generate --watch alongside air, so changes to .templ files are automatically recompiled and the server reloads.
Example Component
// templates/hello.templ
package components
templ Hello(name string) {
<div class="greeting">
<h1>Hello, { name }!</h1>
</div>
}Usage in a handler:
func HelloHandler(c app.Context) error {
return c.Templ(components.Hello(c.Query("name")))
}CSRF and Method Helpers
The generated project includes Templ helpers for CSRF tokens and method override:
templ CsrfField() {
<input type="hidden" name="_token" value={ templ.Raw(csrf.Token) }/>
}
templ MethodField(method string) {
<input type="hidden" name="_method" value={ method }/>
}Usage in forms:
templ EditForm(task models.Task) {
<form method="POST" action={ templ.URL("/tasks/" + strconv.Itoa(task.ID)) }>
@MethodField("PUT")
@CsrfField()
<input type="text" name="title" value={ task.Title }/>
<button type="submit">Update</button>
</form>
}