Skip to content

Repository files navigation

HTMX library for Go

Go Reference

hxgo provides request-header helpers and response-header builders for HTMX v4. It works with Go's standard net/http package and includes helpers for Echo, Fiber, and Gin.

Version support

This module, github.com/stackus/hxgo/v4, supports HTMX v4. Version 1 remains available for HTMX v2 users at github.com/stackus/hxgo, without the /v4 module suffix.

Installation

The module requires Go 1.27.1 or later.

go get github.com/stackus/hxgo/v4
import "github.com/stackus/hxgo/v4"

Overview

Use request helpers to inspect HTMX request headers and Headers to add HTMX response headers to a standard http.ResponseWriter.

package main

import (
	"net/http"

	"github.com/stackus/hxgo/v4"
)

func handler(w http.ResponseWriter, r *http.Request) {
	if !hx.IsHtmx(r) {
		return
	}

	err := hx.Headers(w,
		hx.Location("/account").
			Target("#content").
			Swap(hx.SwapInnerHtml.IgnoreTitle()),
		hx.Trigger(
			"account-updated",
			hx.Event("toast", map[string]string{"message": "Saved"}),
		),
	)
	if err != nil {
		http.Error(w, "unable to build HTMX response", http.StatusInternalServerError)
		return
	}

	_, _ = w.Write([]byte("<p>Account updated.</p>"))
}

Working with requests

The request helpers read HTMX request headers from *http.Request. Each adapter package exposes the same helpers for its framework context.

Request header Constant Helper
HX-Boosted HxBoosted IsBoosted
HX-Current-URL HxCurrentUrl GetCurrentUrl
HX-History-Restore-Request HxHistoryRestoreRequest IsHistoryRestoreRequest
HX-Request HxRequest IsRequest, IsHtmx
HX-Request-Type HxRequestType GetRequestType, IsRequestTypeFull, IsRequestTypePartial
HX-Source HxSource GetSource
HX-Target HxTarget GetTarget

Is* helpers report whether the relevant header is present, except IsRequestTypeFull and IsRequestTypePartial, which compare HX-Request-Type with full and partial. Get* helpers return an empty string when their header is absent.

func handler(w http.ResponseWriter, r *http.Request) {
	if hx.IsRequestTypePartial(r) {
		// Render a partial response.
		return
	}
	if hx.IsRequestTypeFull(r) {
		// Render a full-page response.
	}
}

Working with responses

Headers applies HeaderOption values to an http.ResponseWriter. It only sets response headers; write the status code and response body with the usual facilities of your HTTP framework after calling it.

func handler(w http.ResponseWriter, r *http.Request) {
	if err := hx.Headers(w,
		hx.Retarget("#messages"),
		hx.Reselect("#message-list"),
		hx.PushUrl("/messages"),
	); err != nil {
		http.Error(w, "unable to build HTMX response", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	_, _ = w.Write([]byte("<ul id=\"message-list\"></ul>"))
}

HeaderOption is the common option interface. HeaderOptionFunc is the function form used by options such as Trigger, Refresh, ReplaceUrl, and PushUrl.

Building headers for another HTTP package

BuildHeaders returns an *HxHeaders without writing a response. Use HxHeaders.Headers() to obtain the constructed header map when integrating with a package that does not use http.ResponseWriter.

headers, err := hx.BuildHeaders(
	hx.Redirect("/sign-in"),
	hx.Trigger("session-expired"),
)
if err != nil {
	return err
}

for name, value := range headers.Headers() {
	// Set name and value with the target framework's response API.
	_ = name
	_ = value
}

Response headers and options

Response header Constant Option
HX-Location HxLocation Location
HX-Redirect HxRedirect Redirect
HX-Refresh HxRefresh Refresh
HX-Retarget HxRetarget Retarget
HX-Reswap HxReswap Reswap or a Swap* constant
HX-Reselect HxReselect Reselect
HX-Replace-Url HxReplaceUrl ReplaceUrl
HX-Push-Url HxPushUrl PushUrl
HX-Trigger HxTrigger Trigger

Redirect, Retarget, Reswap, and Reselect are string types that can be passed directly to Headers. Refresh() sets HX-Refresh: true. ReplaceUrl and PushUrl accept either a string URL or false to disable the corresponding history behavior.

err := hx.Headers(w,
	hx.Redirect("/sign-in"),
	hx.Refresh(),
	hx.ReplaceUrl(false),
	hx.PushUrl("/projects"),
)

Location

Calling Location(path) sets HX-Location. With only a path, it writes that path directly. Calling a fluent method makes the header a JSON object containing the path and configured properties.

Method Location property
Source(string) source
Event(string) event
Target(string) target
Swap(string | Reswap | *ReswapModifiers) swap
Transition() transition: "true"
Values(any) values
Headers(map[string]string) headers
Select(string) select
SelectOOB(string) selectOOB
err := hx.Headers(w,
	hx.Location("/search").
		Source("button#search").
		Event("click").
		Target("#results").
		Swap(hx.SwapInnerMorph.Transition()).
		Select("#results").
		SelectOOB("#notifications").
		Values(map[string]string{"query": "go"}).
		Headers(map[string]string{"X-Requested-With": "search"}),
)

Reswap

Reswap overrides the swap strategy selected by the triggering element. Pass a custom Reswap string or one of these constants:

Constant Swap strategy
SwapInnerHtml Replace an element's content.
SwapOuterHtml Replace an entire element.
SwapTextContent Replace text without parsing HTML.
SwapBefore Insert before an element.
SwapPrepend Insert as the first child.
SwapAppend Insert as the last child.
SwapAfter Insert after an element.
SwapInnerMorph Morph an element's content.
SwapOuterMorph Morph an entire element.
SwapOuterSync Morph target attributes, then replace children.
SwapDelete Remove the target.
SwapNone Do not insert response content.
SwapUpsert Update elements by ID and insert new elements.

Modifiers are fluent and can be chained:

Modifier Behavior
Transition() Enable view transitions.
Swap(string | time.Duration) Set the swap delay.
Settle(string | time.Duration) Set the settle delay.
IgnoreTitle() Keep the current document title.
ScrollTop() / ScrollBottom() Scroll to the target edge; pass true for the window or a selector for another target.
ShowTop() / ShowBottom() Show the target edge; optionally pass another selector.
ShowNone() Disable automatic show scrolling.
FocusScroll(bool) Control scrolling when restored focus is brought into view.
Target(string) Set the swap target.
Strip(bool) Control removal of the response's outer element.
SwapEmpty(bool) Control swaps for an empty response.
err := hx.Headers(w,
	hx.SwapOuterMorph.
		Transition().
		Swap("200ms").
		Settle("100ms").
		IgnoreTitle().
		ScrollTop("#results").
		ShowNone().
		FocusScroll(true).
		Target("#content").
		Strip(false).
		SwapEmpty(true),
)

Trigger events

Trigger sets HX-Trigger. It accepts simple event names as strings, TriggerEvent values returned by Event, or both. A simple event is emitted as a comma-separated name. Event(name) creates a named event; one data value is encoded directly, while two or more are encoded as an array.

err := hx.Headers(w,
	hx.Trigger(
		"refresh-sidebar",
		hx.Event("toast", map[string]string{"message": "Saved"}),
		hx.Event("selected", "first", "second"),
	),
)

Framework adapters

The hxecho, hxfiber, and hxgin packages provide the same request-header constants/helpers and a Headers function for their framework context. They re-export response constants, option types, and convenience functions needed to build headers with the adapter package.

Framework Import path Response helper
Echo v5 github.com/stackus/hxgo/v4/hxecho hxecho.Headers(*echo.Context, ...)
Fiber v3 github.com/stackus/hxgo/v4/hxfiber hxfiber.Headers(fiber.Ctx, ...)
Gin github.com/stackus/hxgo/v4/hxgin hxgin.Headers(*gin.Context, ...)

For example, with Echo v5:

package main

import (
	"net/http"

	"github.com/labstack/echo/v5"
	"github.com/stackus/hxgo/v4/hxecho"
)

func main() {
	e := echo.New()
	e.GET("/", func(c *echo.Context) error {
		if hxecho.IsHtmx(c) {
			if err := hxecho.Headers(c,
				hxecho.Location("/account").Target("#content"),
				hxecho.Trigger("account-loaded"),
			); err != nil {
				return err
			}
		}
		return c.String(http.StatusOK, "Hello Echo")
	})
	e.Logger.Fatal(e.Start(":8080"))
}

Contributing

Contributions are welcome. Please open an issue or submit a pull request. Bug reports should include a clear description, reproduction steps, and useful logs or screenshots. Please check for existing issues before creating a new one.

License

This project is licensed under the MIT License. See LICENSE.

About

HTMX request and response helpers for Go

Topics

Resources

Stars

24 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages