HTMX
HTMX is a HTML-over-the-wire JavaScript library that extends HTML with attributes to facilitate AJAX requests, WebSocket connections, CSS Transitions, and server-sent events directly in HTML.
Core Attributes
hx-get
issues a GET to the specified URL
hx-post
issues a POST to the specified URL
hx-on*
handle events with inline scripts on elements
hx-push-url
push a URL into the browser location bar to create history
hx-select
select content to swap in from a response
hx-select-oob
select content to swap in from a response, somewhere other than the target (out of band)
hx-swap
controls how content will swap in (outerHTML
, beforeend
, afterend
, …)
hx-swap-oob
mark element to swap in from a response (out of band)
hx-target
specifies the target element to be swapped
hx-trigger
specifies the event that triggers the request
hx-vals
add values to submit with the request (JSON format)
Swapping with hx-swap=...
innerHTML:
puts the content inside target element (default)outerHTML
: replaces entire target element with returned contentafterbegin
: prepends content before first child inside the targetbeforebegin:
prepends content before target in target's parent elementbeforeend
: appends content after last child inside targetafterend
: appends content after target in target's parent elementdelete
: deletes target element regardless of responsenone
: does nothing (oob-swaps and Response Headers are still processed)
Swap Options (e.g. hx-swap="outerHTML ignoreTitle:true"
):
transition
: true/false, whether to use view transition API for this swapswap
: swap delay (e.g. 100ms) between clearing old and inserting newsettle
: settle delay (e.g. 100ms) between inserting new and settlingignoreTitle
: if true, titles in new content will be ignoredscroll
: top/bottom, will scroll the target element to its top or bottomshow
: top/bottom, will scroll target element's top or bottom into view
HTMX and Go
<!DOCTYPE html> <html> <head> <title>htmx example</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <script src="https://unpkg.com/htmx.org@1.9.2"></script> </head> <body data-bs-theme="dark"> <ul class="list-group" id="film-list"> {{ range . }} {{ block "film-list-element" . }} <li class="list-group-item"> {{ .Title }} - {{ .Director }} </li> {{ end }} {{ end }} </ul> <!-- form to add a film to existing list --> <form hx-post="/add" hx-target="#film-list" hx-swap="beforeend"> <div class="mb-2"> <label for="title">Title</label> <input type="text" class="form-control" id="title" name="title"> </div> <div class="mb-3"> <label for="director">Director</label> <input type="text" class="form-control" id="director" name="director"> </div> <button type="submit" class="btn btn-primary">Add Film</button> </form> </body> </html>
package main import ( "log" "net/http" "html/template" ) type Film struct { Title string Director string } func main() { log.Println("Server started on: http://localhost:8000") tmpl := template.Must(template.ParseFiles("index.html")) films := map[string] Film { "The Shawshank Redemption": {"The Shawshank Redemption", "Frank Darabont"}, "The Godfather": {"The Godfather", "Francis Ford Coppola"}, } http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) { tmpl.Execute(w, films) }) http.HandleFunc("/add", func (w http.ResponseWriter, r *http.Request) { log.Println("htmx Add request received") log.Println(r.Header.Get("HX-Request")) if r.Method == "POST" { title := r.PostFormValue("title") director := r.PostFormValue("director") tmpl.ExecuteTemplate(w, "film-list-element", Film{title, director}) } }) log.Fatal(http.ListenAndServe(":8000", nil)) }
Libraries:
Web Security Guidelines
Only call routes you control
- Uncontrolled routes could insert malicious
<script>
tags - Only use relative URLs:
hx-get="/events"
Always use an auto-escaping template engine
- Replaces &, <, >, ", … with "&", "<", etc.
- Prevents Cross-Site Scripting (XSS) attacks
Only serve user-generated content inside HTML tags
- No user-defined data in scripts, CSS, or as HTML attributes
Set authentication cookies with Secure
, HttpOnly
, and SameSite=Lax
Secure
: only send the cookie via HTTPSHttpOnly
: don't make the cookie available to JS viadocument.cookie
SameSite=Lax
: mitigate Cross-Site Request Forgery (CSRF)