A type-safe library for working with HTMX in Go.
Less time fiddling with HTTP headers, more time developing awesome Hypermedia-driven applications.
Easily check if requests are from HTMX, and utilize a type-safe, declarative syntax for HTMX response headers to control HTMX behavior from the server.
Uses standard net/http
types.
Has some integration with templ components.
import (
"net/http"
"github.com/angelofallars/htmx-go"
)
func(w http.ResponseWriter, r *http.Request) {
if htmx.IsHTMX(r) {
htmx.NewResponse().
Reswap(htmx.SwapBeforeEnd).
Retarget("#errors").
ReplaceURL("/errors").
Write(w)
}
}
Use go get.
go get github.com/angelofallars/htmx-go
Then import htmx-go:
import "github.com/angelofallars/htmx-go"
You can determine if a request is from HTMX. With this, you can add custom handling for non-HTMX requests.
You can also use this for checking if this is a GET request for the initial (very first) page loads on your website, as initial page load requests don't use HTMX.
func(w http.ResponseWriter, r *http.Request) {
if htmx.IsHTMX(r) {
// logic for handling HTMX requests
} else {
// logic for handling non-HTMX requests (e.g. render a full page for first-time visitors)
}
}
func(w http.ResponseWriter, r *http.Request) {
if htmx.IsBoosted(r) {
// logic for handling boosted requests
} else {
// logic for handling non-boosted requests
}
}
htmx-go takes inspiration from Lip Gloss for a declarative way of specifying HTMX response headers.
Make a response writer with htmx.NewResponse()
, and add a header to it to make the page refresh:
func(w http.ResponseWriter, r *http.Request) {
writer := htmx.NewResponse().Refresh(true)
writer.Write(w)
}
func(w http.ResponseWriter, r *http.Request) {
htmx.NewResponse().
// Override 'hx-target' to specify which target to load into
Retarget("#errors").
// Also override the 'hx-swap' value of the request
Reswap(htmx.SwapBeforeEnd).
Write(w)
}
You can add triggers and let htmx-go take care of formatting and JSON serialization of the header values.
Define event triggers:
htmx.Trigger(eventName string)
- A trigger with no details.htmx.TriggerDetail(eventName string, detailValue string)
- A trigger with one detail value.htmx.TriggerObject(eventName string, detailObject any)
- A trigger with a JSON-serializable detail object. Recommended to pass in eithermap[string]string
or structs with JSON field tags.
Set trigger headers using the triggers above:
Response.AddTrigger(trigger ..EventTrigger)
- appends to theHX-Trigger
headerResponse.AddTriggerAfterSettle(trigger ..EventTrigger)
- appends to theHX-Trigger-After-Settle
headerResponse.AddTriggerAfterSwap(trigger ..EventTrigger)
- appends to theHX-Trigger-After-Swap
header
htmx.NewResponse().
AddTrigger(htmx.Trigger("myEvent"))
// HX-Trigger: myEvent
htmx.NewResponse().
AddTrigger(htmx.TriggerDetail("showMessage", "Here Is A Message"))
// HX-Trigger: {"showMessage":"Here Is A Message"}
htmx.NewResponse().
AddTrigger(
htmx.TriggerDetail("hello", "world"),
htmx.TriggerObject("myEvent", map[string]string{
"level": "info",
"message": "Here Is A Message",
}),
)
// HX-Trigger: {"hello":"world","myEvent":{"level":"info","message":"Here is a Message"}}
HTMX response writers can be declared outside of functions with var
so you can reuse them in several
places.
Caution
If you're adding additional headers to a global response writer, always use the .Clone()
method
to avoid accidentally modifying the global response writer.
var deleter = htmx.NewResponse().
Reswap(htmx.SwapDelete)
func(w http.ResponseWriter, r *http.Request) {
deleter.Clone().
Reselect("#messages").
Write(w)
}
HTMX pairs well with Templ, and this library is no exception. You can render
both the necessary HTMX response headers and Templ components in one step with the
.RenderTempl()
method.
// hello.templ
templ Hello() {
<div>Hello { name }!</div>
}
// main.go
func(w http.ResponseWriter, r *http.Request) {
htmx.NewResponse().
Retarget("#hello").
RenderTempl(r.Context(), w, Hello())
}
Note
To avoid issues with custom HTTP status code headers with this approach,
it is recommended to use Response().StatusCode()
so the status code header
is always set after the HTMX headers.
If you have an element that is polling a URL and you want it to stop, use the
htmx.StatusStopPolling
286 status code in a response to cancel the polling. HTMX documentation
reference
w.WriteHeader(htmx.StatusStopPolling)
If you need to work with HTMX headers directly, htmx-go provides constant values for all HTTP header field names of HTMX so you don't have to write them yourself. This mitigates the risk of writing header names with typos.
// Request headers
const (
HeaderBoosted = "HX-Boosted"
HeaderCurrentURL = "HX-Current-URL"
HeaderHistoryRestoreRequest = "HX-History-Restore-Request"
HeaderPrompt = "HX-Prompt"
HeaderRequest = "Hx-Request"
HeaderTarget = "HX-Target"
HeaderTriggerName = "Hx-Trigger-Name"
)
// Common headers
const (
HeaderTrigger = "HX-Trigger"
)
// Response headers
const (
HeaderLocation = "HX-Location"
HeaderPushURL = "HX-Push-Url"
HeaderRedirect = "HX-Redirect"
HeaderRefresh = "HX-Refresh"
HeaderReplaceUrl = "HX-Replace-Url"
HeaderReswap = "HX-Reswap"
HeaderRetarget = "HX-Retarget"
HeaderReselect = "HX-Reselect"
HeaderTriggerAfterSettle = "HX-Trigger-After-Settle"
HeaderTriggerAfterSwap = "HX-Trigger-After-Swap"
)
This library is compatible with the standard net/http
library, as well as other routers like Chi
and Gorilla Mux that use the standard http.HandlerFunc
handler type.
With the Echo web framework, try passing in context.Request()
and
context.Response().Writer
for requests and responses, respectively.
With the Gin web framework on the other hand, try using context.Request
and
context.Writer
.
Pull requests are welcome!