GO+HTMX, Why Not!

Giving Go+HTMX a try to get excited about front-end again
Published on 2024/02/04

After some React fatigue (I shared a brief thought about it in React, my old Friend) I played around with HTMX. I built a small server for a budgeting app, all it does is list transactions, allow you to add new ones and change categories associated with each one. Without looking at the docs too much I was able to build some basics really quick. Listing and adding transactions was a breeze!

This feels familiar and if memory serves me well, I had a small project in college where I used PHP with templating to build a small back-office service. What's old is new again! The gist of it all is the ability to do little to no JS to build a web UI. With HTMX you decide which html data needs to be requested to your server so that it can directly replace a portion of your existing html.

As a trivial example, think of a list of TODO items, transactions, really any list.

<div>
  <h2>Add Transaction</h2>
  <form
    hx-post="/add-transaction"
    hx-target="#transactions-list"
    hx-swap="beforeend"
    hx-on::after-request="if(event.detail.successful) this.reset()"
  >
    <div>
      <label for="transaction-vendor">Vendor</label>
      <input type="text" name="vendor" id="transaction-vendor" />
    </div>
    <div>
      <label for="transaction-amount">Amount</label>
      <input type="float" name="amount" id="transaction-amount" />
    </div>
    <button type="submit">Submit</button>
  </form>
  <h2>Transactions</h2>
  <ul id="transactions-list">
    {{ range.Transactions }} {{ block "transaction-list-element" .}}
    <li>
      <span>{{ .Vendor }}</span>
      <span>{{ .Amount }}</span>
    </li>
    {{ end }} {{ end }}
  </ul>
</div>

If you pay attention to the form element, that's where the fun begins. You specify that the form submission triggers a POST request. The result of that request will be appended to the element with id transactions-list. On your server you then need the corresponding path to return the correct html:

mux.HandleFunc("/add-transaction", func(w http.ResponseWriter, r *http.Request) {
  vendor := r.PostFormValue("vendor")
  amountStr := r.PostFormValue("amount")

  amount, err := strconv.ParseFloat(amountStr, 32)
  if err != nil {
    w.WriteHeader(http.StatusBadRequest)
    return
  }

  tmpl := template.Must(template.ParseFiles("index.html"))
  tmpl.ExecuteTemplate(
    w,
    "transaction-list-element",
    Transaction{Vendor: vendor, Amount: float32(amount)},
  )
})

and if you look at the network tab on your browser, the response is:

<li>
  <span>test</span>
  <span>12</span>
</li>

and HTMX takes care of the rest.

Thoughts

I will play with this more and try to push the boundaries of it. I realize that it will work pretty well for class-based CSS frameworks like Tailwind. And I appreciate the fact that it forces you to brush up on plain HTML.

0
← Go Back