A concise guide on using HTMX with Go’s
net/http
and
html/template
.
HTMX is a lightweight JavaScript library that makes it
possible to add dynamic, partial page updates to your web
app by using simple attributes in your HTML. In this
tutorial, you’ll see how to integrate HTMX with a minimal Go
backend.
Now, HTMX is pretty hard to grasp (it took me forever), so
make sure to follow this step by step. Remember, you can
always look at the git repo for examples and to see how
everything is implemented.
A small folder layout might look like this:
myproject/
├── go.mod
├── main.go
└── templates/
├── index.html
- go.mod: Go module/dependency file
- main.go: Contains your Go server code.
- templates: Houses any HTML templates,
including index.html
. For now, it's alone, but in the future we'll add more slices.
To use HTMX, just add its script tag to your HTML. In a
basic setup, you can include this in the header in
index.html
:
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous">
</script>
Using a CDN like this is good for trying it out, but it has flaws. Look at the HTMX docs for proper installation.
hx-get
,
hx-post
, hx-target
, etc., become
available in your HTML, letting you request server data and
update the page without a full reload.
Here’s a simple index.html
that shows a button
using hx-get
to fetch content from
/snippet
and display it in a
<div>
:
{{define "index.html"}}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>HTMX + Go Example</title>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"> </script>
</head>
<body>
<h1>Minimal HTMX Demo</h1>
<button
hx-get="/snippet"
hx-target="#snippet-area"
hx-swap="innerHTML">
Load Snippet
</button>
<div id="snippet-area" style="margin-top: 1rem; border: 1px solid #ccc; padding: 1rem;">
<!-- Updated by the snippet response -->
</div>
</body>
</html>
{{end}}
When clicked, the button sends a GET request to
/snippet
and injects the server’s response into
#snippet-area
.
Below is a Go server that:
templates/
index.html
at /
/snippet
package main
import (
"fmt"
"html/template"
"log"
"net/http"
)
var tmpl *template.Template
func main() {
// Parse all .html files in templates folder
var err error
tmpl, err = template.ParseGlob("templates/*.html")
if err != nil {
log.Fatalf("Error parsing templates: %v", err)
}
http.HandleFunc("/", indexHandler)
http.HandleFunc("/snippet", snippetHandler)
fmt.Println("Server running on http://localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
// Render index.html
if err := tmpl.ExecuteTemplate(w, "index.html", nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func snippetHandler(w http.ResponseWriter, r *http.Request) {
// Return a small HTML snippet
snippet := "<p> This snippet loaded via HTMX! <p>"
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(snippet))
}
That’s it! When you click the button on
index.html
, the
snippetHandler
returns a bit of HTML that gets
injected into #snippet-area
.
1. In your project folder, run
go run main.go
.
2. Open
http://localhost:8080
in your browser.
3. Click “Load Snippet” – you should
see the green snippet inserted without a full page reload.
With just a few lines of code, you’ve added dynamic, partial
page updates to a Go application using
HTMX
.
There’s no heavy JavaScript framework here—just simple HTML
attributes. From here, you can extend the snippet approach
for forms, multi-step interactions, or inline editing.