GO + HTMX + Templ

GO + HTMX + Templ

I have been using a javascript framework called Hono with HTMX, it’s a simple and easy to use framework that provides support for JSX, making it easy when working with HTMX.

Fortunately there is a similiar package in Go that works in a similiar fashion.

Usually gophers try sticking to what is available in the standard library, but the provided html templating package doesn’t cut it.

   const tpl = `
        <h1>{{.Title}}</h1>`
    t, err := template.New("webpage").Parse(tpl)
    err = t.Execute(os.Stdout, struct{ Title string }{"Hello, World!"})

While capable, it is clunky to use and is not as convenient as something like JSX.

Templ provides a similiar dx to JSX, you can write go code and html together in a templ file, like JSX allowing you to use language features to build your html, making it easier to create and read.

Templ supports template composition, we can create a base layout with our main html structure.

package views

templ Layout(title string) {
    <!DOCTYPE html>
	<html lang="en">
		<head>
			<meta charset="utf-8"/>
			<meta name="viewport" content="width=device-width"/>
			<script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio"></script>
			<script src="https://unpkg.com/[email protected]"></script>
			<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
			<title>{ title }</title>
		</head>
		<body>
			<main class="container mx-auto" id="main">
				{ children... }
			</main>
		</body>
	</html>
}

This is a .templ, when compiled it will generate a go file and a function Layout that can be imported from views.

A templ function returns a type with a Render method that can be called to render the html. It takes a context and a writer, allowing you to write the html to a file or a http response.

type Component interface {
	Render(ctx context.Context, w io.Writer) error
}

We will create a clock component that updates the time from the server every second.

package views

templ TimerPage() {
    @Layout("Timer") {
        <div>
            <h1>Current time</h1>
            <div hx-get="/timer" hx-trigger="every 1s"></div>
        </div>
    }
}

Here is our timer page, wrapped with our layout component. Not the htmx attributes that will call /timer endpoint every second.

package views

import "time"

templ Timer() {
    <time>{time.Now().Format("15:04:05")}</time>
}

Our timer component will render the current server time.

Using the echo web framework, here is our server implementation:

func main() {
	e := echo.New()
	e.GET("/", internal.HomeHandler)
	e.GET("/timer", internal.TimerHandler)
	e.Logger.Fatal(e.Start(":8080"))
}

func HomeHandler(c echo.Context) error {
	return views.TimerPage().Render(c.Request().Context(), c.Response())
}

func TimerHandler(c echo.Context) error {
	return views.Timer().Render(c.Request().Context(), c.Response())
}

Install

Add the templ package to your go project:

go get github.com/a-h/templ

Install the templ cli:

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

Compile your .templ files:

templ generate

You can create a makefile to generate your templates and run your server:

default:
	@templ generate
	@go run ./cmd/app/main.go
© 2024 Timney.