💾 Archived View for capsule.adrianhesketh.com › 2021 › 05 › 18 › introducing-templ captured on 2022-06-11 at 20:56:31. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-11-30)
-=-=-=-=-=-=-
Templ is a language, command line tool and set of IDE extensions that makes it easier to write HTML user interfaces and websites using Go. It strives for simplicity, developer experience and performance - in that order.
The command line tool generates Go code from the templates (`templ generate`) provides formatting (`templ fmt`) and supports the VS Code and vim extensions by providing a Language Server implementation for autocomplete (`templ lsp`).
I enjoy using the Go programming language, but I've never been a fan of the built-in Go templating language. It's hard to call external functions (you have to remember to add them), templates can crash at runtime because they're not strongly typed, I can never remember the syntax, and the IDE support isn't great.
It does a job, but I couldn't see myself using it to build a project, so I looked at the alternatives in the Go ecosystem. I liked a few, but the IDE support was lacking (and would have been hard to retrofit) or they weren't strongly typed, or compiled, so I decided to start a project squarely focussed at building HTML components, that has great IDE support as a core component.
My plan is to use templ as the templating language in Hotwire [0] style projects, to compare it to established SPA, micro-site and micro-frontend tooling.
You can watch a short video example on YouTube [1] or you can follow the steps yourself.
To run it on your machine, install templ by downloading the latest binary for your system from Github [2]. You'll need to place the binary somewhere that's in your command line path, e.g. (the `/usr/local/bin` directory on a Mac, on Windows, you can check the path with `echo %PATH%`).
Create a new Go project.
mkdir hello-world cd hello-world go mod init github.com/a-h/templ-hello-world
Create your first template file - `hello.templ`.
{% package main %} {% templ Page(title string, content templ.Component) %} <html> <head> <title>{%= title %}</title> </head> <body> {%! content %} </body> </html> {% endtempl %} {% templ Greeter(name string) %} <div> <h1>{%= "Hello, " + name + "!" %}</h1> </div> {% endtempl %}
Generate your Go code.
templ generate
Now build a web server by creating `main.go`:
package main func main() { http.Handle("/hello", HelloHandler{}) http.ListenAndServe(":8000", nil) } type HelloHandler struct{} func (h HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Get the data. name := r.URL.Query().Get("name") // Create the components. body := Greeter(name) page := Page("hello", body) // Render. err := page.Render(r.Context(), w) if err != nil { log.Println("error", err) } }
Run it.
go run *.go
Visit in a web browser.
http://localhost:8000/hello?name=World
HTML user interfaces and websites are often built up from individual components, such as buttons, textboxes, and screen areas. These components need to populated with text that comes from various sources, such as APIs or database calls.
Templ makes it easy to construct HTML components using a tiny language that compiles to Go code. The output of a templ component is automatically HTML escaped and minified to reduce bandwidth requirements. Compiling to Go code means that the components are rendered quickly and that more errors are caught at compilation time instead of runtime.
The templ language is a cut-down HTML interspersed with Go branching expressions (e.g. `if`, `else`, `switch`, for`), string outputs and calls to external templates.
The project README documents the language, but a complete example is probably all you need to get started.
{% import "strings" %} {% templ Layout(header, footer, body templ.Component) %} {%! header %} {%! body %} {%! footer %} {% endtempl %} {% templ AddressTemplate(addr Address) %} <div>{%= addr.Address1 %}</div> <div>{%= addr.Address2 %}</div> <div>{%= addr.Address3 %}</div> <div>{%= addr.Address4 %}</div> {% endtempl %} {% templ PersonTemplate(p Person) %} <div> <div>{%= p.Name() %}</div> <a id="id123" href={%= p.URL %}>{%= strings.ToUpper(p.Name()) %}</a> <div> {% if p.Type == "test" %} <span>{%= "Test user" %}</span> {% else %} <span>{%= "Not test user" %}</span> {% endif %} {% for _, v := range p.Addresses %} {%! AddressTemplate(v) %} {% endfor %} {% switch p.Type %} {% case "test" %} <span>{%= "Test user" %}</span> {% endcase %} {% case "admin" %} <span>{%= "Admin user" %}</span> {% endcase %} {% default %} <span>{%= "Unknown user" %}</span> {% enddefault %} {% endswitch %} </div> </div> {% endtempl %}
templ components implement a simple interface containing a method that writes the component to any `io.Writer`, for example, a `http.ResponseWriter`, an `os.File`, or a `strings.Buffer`. This makes templ components both flexible, and able to be used alongside other tools.
It's possible to write templ components using the templ language, or by directly writing Go code that implments the `templ.Component` interface.
{% templ Hello(name string) %} <div>{%= name %}</div> {% endtempl %}
type Hello struct { Name string } func (h Hello) Render(ctx context.Context, w io.Writer) (err error) { _, err = io.WriteString("<div>" + html.EscapeString("Hello, " + h.Name + "!") + "</div>") return err }
func Hello(name string) templ.Component { return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { _, err = io.WriteString("<div>" + html.EscapeString("Hello, " + name + "!") + "</div>") return }) }
Templ components can be passed as arguments, or embedded within the template to build complex layouts:
{% templ Layout(url string, header, footer, body templ.Component) %} {%! header %} {%! SideBar(url) %} {%! body %} {%! footer %} {% endtempl %}
Since templ components are just Go code, they can be packaged up as a Go module and shared like any other - by pushing the code to Github or another source code provider.
To version compnents, tag the git repository with a version number.
export VERSION=`git rev-list --count HEAD`; git tag v0.0.${VERSION}; git push origin v0.0.${VERSION};
To import components, run `go get github.com/user/library-name`, add `import` statements to your `.templ` or `.go` files, and you're good to go.
Templ ships as a command line tool that generates Go code from templ code (`templ generate`), formats templ code (`templ fmt`) and provides features used by IDE plugins and extensions.
A VS Code extension [3] and vim plugin [4] provide syntax highlighting and autocomplete for developers.
Use Go's standard library functions that return a string.
{% templ PersonAge(p Person) %} <div> {%= strconv.Itoa(p.Age) %} </div> {% endtempl %}
Since templ components are just Go functions, if your needs are more complex, you can also write a component to do it.
func Number(d int64) templ.Component { return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { _, err = io.WriteString(strconv.Itoa(d)) return err }) }
{% templ PersonAge(p Person) %} <div> {%! Number(p.Age) %} </div> {% endtempl %}
Trying out npm and yarn workspaces