Today there is a plethora of web frameworks that use client-side javascript to create reactive websites. It has become quite difficult to choose one of them, they all look amazing and offer so many features. But in a way they hide things from you, you don’t really know how it all fits together. So let us see what else we can use to build a web application. We will go back to the roots and introduce TEMPL, a Go package developed by Adrian Hesketh that goes beyond the standard html/template library. It’s useful to create simple static web components that can be integrated into a bigger picture and served by a compiled binary. Assembling small components yourself helps to keep control of your code.

Installation

Installing a package in Go is as simple as

go install github.com/a-h/templ/cmd/templ@latest

A small templ example

Traditionaly let start by initializing a new Go module and fetching templ

mkdir welcome
cd welcome
# create a go.mod file to manage dependencies
# update below the unique identifier of your module
go mod init github.com/<your_handle>/welcome
# download and install templ
go get github.com/a-h/templ

Create your first template

vi welcome.templ
package main

templ welcome(firstname string) {
<h1>Welcome to yet emerging technologies, { firstname } !</h1>
}

You can now process your template to generate Go code from it

templ generate

To watch current directory for changes and regenerate constantly

templ generate -watch

But code built like that isn’t optimized for production use

You should now have a welcome_templ.go file containing your newly generated Go function named welcome with the following signature, it returns templ.Component an interface that has a Render method on it that is used to render the component to an io.Writer.

func welcome(firstname string) templ.Component {

returnedtempl.Component can be called like that

component := welcome("Sebastien")

to generate the expected content wherever you want for example on Stdout

component.Render(context.Background(), os.Stdout)

So it looks like that

package main

import (
"context"
"os"
)

func main() {
  component := welcome("Sebastien")
  component.Render(context.Background(), os.Stdout)
}

When you compile and run it you should see

go run .
<h1>Welcome to yet emerging technologies, sebastien</h1>

Writing HTML snippets on Stdout isn’t a dream job so let’s instead serve it thru HTTP using the net/http package

package main

import (
  "fmt"
  "net/http"

  "github.com/a-h/templ"
)

func main() {
  component := welcome("sebastien")

  http.Handle("/", templ.Handler(component))

  fmt.Println("Listening on :3000")
  http.ListenAndServe(":3000", nil)
}

Test it to see if everything looks good ?

curl http://localhost:3000
<div>Welcome to yet emerging technologies, sebastien</div>

Our Hello world works great! As you’ve seen, templ generates functions that output your snippets. Remember that you can benefit from the power of Go in your template (if, switch, etc…).

templ files can also contain call to other components or Go code to implement some logic processing outside the templ declaration which will be considered as Go ordinary code.

So to summarize, components are markup and Go code that is compiled into Go functions that return a templ.Component interface by running the templ generate command.

Syntax cheatsheet

  • All Tags must be close: <br/>, <div></div>, <button></button>
  • strings values are automatically escaped (security measure)
    • & characters in the URL are escaped to &amp;.
    • " characters are escaped to &quot;.
    • ' characters are escaped to &#39;.
  • It’s possible to use function calls in components string attribute expressions

Below you’ll find a cheatsheet of what can be used within the templ declaration.

tag description
{ variable } replaced by received value for variable
@component call a component or inject the rendered output of a component h
function call need to return string or (string, error)
?={ true | false } only output preceding attribute if true
if/else reserved keywords, Capitalize or put in String to avoid statement
switch reserved keywords, Capitalize or put in String to avoid statement
for reserved keywords, Capitalize or put in String to avoid statement
{{ ... }} raw go to be excuted

Web Application Structure

Hopefully you should now have a better understanding of the usefulness of this templating engine, let’s share what is the recommended structure for a web app using this module. Counter, available in the repository is a good example of that.

  • All of you components should live in their own components package.
  • Database related interactions are also in their own db package
  • Same for your handlers, services used by your handlers and session which implement HTTP session IDs

Finally a main.go will glue all of that together. So the directory structure should look something like that

components/
db/
handlers/
services/
session

{ attrMap... }

By using a syntax like { attrMap... } it becomes possible to spread any variable of type templ.Attributes a map[string]any type definition. Quite useful to define a list of arguments in a single map which will be spread out when generating the component. The way it will be spread out depends on the type of values within the key of the map. Let see what will happen for each types

  • if value is a string: <div name="value">
  • if value is a bool: <div name> if true
  • if value is a templ.KeyValue[string, bool]: `
    if true
  • if value templ.KeyValue[bool, bool]: <div name> if both true

URLs

href require a templ.SafeURL instead of just a string, you can use templ.URL to sanitizes your string

templ component(p Person) {
<a href={ templ.URL(p.URL) }>{ strings.ToUpper(p.Name) }</a>
}

Also URLs sanitised used outside of href or <form action="" should be converted back to strings

<div hx-get={ string(templ.URL(fmt.Sprintf("/contacts/%s/email", contact.ID)))}>

Composition

Imagine you have a layout that you want to apply to all your pages

templ heading() {
	<h1>Heading</h1>
}

templ layout(contents templ.Component) {
	<div id="heading">
		@heading()
	</div>
	<div id="contents">
		@contents
	</div>
}

templ paragraph(contents string) {
<p>{ contents }</p>
}

You can now inject your content into your layout

package main

import (
	"context"
	"os"
)

func main() {
	c := paragraph("Dynamic contents")
	layout(c).Render(context.Background(), os.Stdout)
}

you can also pass templ components as parameters to other components

templ root() {
	@layout(paragraph("Dynamic contents"))
}

components can be joined together

templ helloWorld() {
	@templ.Join(hello(), world())
}

If you want to reuse a component outside it’s package, it’s first letter should be a capital letter. package components

Package components

templ Hello() {
	<div>Hello</div>
}

Importing Javascript

To import Javacript you just have to add something like that in your head component.

templ head() {
	<head>
		<script src="https://unpkg.com/lightweightcharts/dist/lightweight-charts.standalone.production.js"></script>
	</head>
}

You can then use the imported JavaScript in any templ via a <script> tag.

To serve the file you can assign a new Handler for your static assets and serve it, all of that using the net/http standard library.

mux := http.NewServeMux()
mux.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets"))))
http.ListenAndServe("localhost:8080", mux)

Importing HTMX

HTMX can be used to selectively replace content within a web page. To make it available to your project you just have to download htmx.min.js and serve it. You can then add a <script> tag to the <head> section of your HTML, like we’ve shown above.

<script src="/assets/js/htmx.min.js"></script>

Context

allows to pass parameters down the chain of components using the implicit ctx variable

// Define the context key type.
type contextKey string

// Create a context key for the theme.
var themeContextKey contextKey = "theme"

// Create a context variable that inherits from a parent, and sets the value "test".
ctx := context.WithValue(context.Background(), themeContextKey, "test")

// Pass the ctx variable to the render function.
themeName().Render(ctx, w)

it is then accessible from within components like thtat

templ themeName() {
	<div>{ ctx.Value(themeContextKey).(string) }</div>
}

Standard modules imported by TEMPL

templ import the following standard modules

Dependencies (37)

Go module dependency management will also install the following packages

  • x/crypto supplementary Go cryptography packages.
  • x/sync supplementary Go concurrency primitives
  • x/net supplementary Go networking packages
  • x/sys supplementary Go packages for low-level interactions with the operating system
  • x/tools various tools and packages mostly for static analysis of Go programs
  • x/telemetry Go Telemetry server code and libraries
  • x/term Go terminal and console support packages.
  • x/text supplementary Go packages for text processing
  • x/xerrors transition packages for the new Go 1.13 error values
  • yaml- YAML support for the Go language
  • asm algorithms that use the full power of modern CPUs to get the best performance.
  • goquery a set of features similar to jQuery
  • spew implements a deep pretty printer for Go data structures to aid in debugging.
  • Color lets you use colorized outputs in terms
  • jsonrpc2 provides a Go implementation of JSON-RPC 2.0.
  • Zap provides fast, structured, leveled logging
  • atomic a go package for atomic file writing
  • backoff a Go port of the exponential backoff algorithm
  • browser helpers to open URLs, readers, or files in the system default web browser.
  • fsnotify cross-platform filesystem notifications on Windows, Linux, macOS, BSD, and illumos.
  • mod for direct manipulation of Go modules themselves.
  • brotlibrotli compressor and decompressor
  • parsea set of parsing tools for Go inspired by Sprache.
  • uri an implementation of the URI Uniform Resource Identifier(RFC3986)
  • go-isatty test whether a file descriptor refers to a terminal
  • go-colorable colorable writer for windows.
  • encoding implementations of encoders and decoders for various data formats
  • multierr allows combining one or more Go errors together
  • cascadia implements CSS selectors for use with the parse trees produced by the html package
  • cmp determines equality of values
  • kr/text for manipulating paragraphs of text.
  • pmezard/go-difflib partial port of python 3 difflib package
  • rs/cors a net/http handler implementing Cross Origin Resource Sharing W3 specification in Golang
  • stretchr/objx for dealing with maps, slices, JSON and other data
  • stretchr/testify provide many tools for testifying that your code will behave as you intend
  • yuin/goldmark - A Markdown parser written in Go. Easy to extend, standards-compliant, well-structured.
  • goleak Goroutine leak detector to help avoid Goroutine leaks.

I know I said simple static page, but remember that your Go code will be statically compiled, so all these dependencies are hidden from your end user. At least we know the building blocks of templ. I always like to break things down to understand the inner workings of things. Iif you ever want to see the above list of dependencies, enter the module and run

go list -m all

I hope this article was useful, I’ll experiment more and may update this article in the future.