Minimal HTMX + Go Tutorial

A concise guide on using HTMX with Go’s net/http and html/template.

1. Overview

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.

2. Project Structure

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.

3. HTMX Setup

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.



Once loaded, HTMX attributes like 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.

4. A Minimal HTML Example

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.

5. Minimal Go Code (main.go)

Below is a Go server that:


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.

6. Testing the App

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.

7. Conclusion

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.