htmx

package module
v1.27.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 4, 2026 License: MIT Imports: 10 Imported by: 2

Documentation

Overview

Package htmx provides HTMX integration helpers including error handling patterns and response utilities for building dynamic web applications.

The tc-btn-loading indicator hook

LoadingButton wraps its output in a `<span class="tc-btn-loading">`. That class is a deliberately public, stable hook for hx-indicator scoping: by default HTMX toggles `.htmx-indicator` visibility relative to the REQUESTING element, but when the request fires from a different element (e.g. a form submit driving a toolbar button), point hx-indicator at the wrapper:

<button hx-post="/save" hx-indicator="closest .tc-btn-loading">
   @htmx.LoadingButton("Save", "Saving…", spinner)
</button>

Do not remove or rename the class — consumer CSS and hx-indicator selectors depend on it (guarded by htmx snapshot tests + the demo's visualtest/loading_button_e2e_test.go).

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CSRFToken

func CSRFToken(token string) templ.Component

CSRFToken renders a hidden input for HTMX forms with CSRF protection

@htmx.CSRFToken("csrf_token_value")
Example
package main

import (
	"github.com/larsartmann/templ-components/htmx"
)

func main() {
	_ = htmx.CSRFToken("csrf-token")
}

func ConfirmDelete

func ConfirmDelete(props ConfirmDeleteProps) templ.Component

ConfirmDelete renders a button with a confirmation dialog for destructive actions. HTMX-ONLY by design: the pinned Datastar runtime ships NO confirm machinery (bundle-verified 2026-10-03 — zero confirm tokens in the v0.6.1 bundle), so there is no wire-dialect twin. A Datastar caller that needs confirmation owns it consumer-side (e.g. a data-on:click__prevent handler that re-fires the action after its own dialog resolves).

@htmx.ConfirmDelete(htmx.ConfirmDeleteProps{Delete: "/delete/123", Target: "#item-123", Confirm: "Are you sure?"})
Example
package main

import (
	"github.com/larsartmann/templ-components/htmx"
)

func main() {
	_ = htmx.ConfirmDelete(htmx.ConfirmDeleteProps{Delete: "/items/1", Target: "#list"})
}

func GlobalErrorHandling

func GlobalErrorHandling(cfg ErrorHandlingConfig) templ.Component

GlobalErrorHandling renders a comprehensive global HTMX error handling script. Include this once in your base layout, ideally at the end of the body. Requires feedback.ToastContainer on the page for accessible error announcements (aria-live). If ToastContainer is not present, errors are only logged to the console.

Typical layout wiring:

@layout.Base(layout.PageProps{...}) {
    @feedback.ToastContainer(nonce)  // must be present for toast error feedback
    // ... page content ...
    @htmx.GlobalErrorHandling(htmx.DefaultErrorHandlingConfig())
}
Example
package main

import (
	"github.com/larsartmann/templ-components/htmx"
)

func main() {
	_ = htmx.GlobalErrorHandling(htmx.DefaultErrorHandlingConfig())
}

func InlineLoadingOverlay

func InlineLoadingOverlay(id string, spinner templ.Component) templ.Component

InlineLoadingOverlay renders a localized loading overlay for HTMX targets. Accepts a spinner component for decoupled loading animation.

@htmx.InlineLoadingOverlay("tc-form-loading", feedback.Spinner(feedback.SpinnerProps{Size: feedback.SpinnerMD, Color: "text-blue-600 dark:text-blue-400"}))
Example
package main

import (
	"github.com/larsartmann/templ-components/htmx"
)

func main() {
	_ = htmx.InlineLoadingOverlay("results", nil)
}

func LoadingButton

func LoadingButton(defaultText, loadingText string, spinner templ.Component) templ.Component

LoadingButton renders a button with built-in loading state. During an HTMX request (htmx-request on the button or an ancestor) the default text hides and the loading text + spinner show; at rest only the default text is visible. The spinner is wrapped in htmx-indicator so it is gated regardless of the color classes passed in. The tc-btn-loading wrapper is a stable hook for hx-indicator when a different indicator scope is needed.

<button class="btn-primary" hx-post="/save">
   @htmx.LoadingButton("Save", "Saving...", feedback.Spinner(feedback.SpinnerProps{Size: feedback.SpinnerSM, Color: "text-white"}))
</button>
Example
package main

import (
	"github.com/larsartmann/templ-components/htmx"
)

func main() {
	_ = htmx.LoadingButton("Save", "Saving...", nil)
}

func LoadingIndicator

func LoadingIndicator(spinner templ.Component) templ.Component

LoadingIndicator renders a fixed-position HTMX loading indicator. Accepts a spinner component for decoupled loading animation.

@htmx.LoadingIndicator(feedback.Spinner(feedback.SpinnerProps{Size: feedback.SpinnerLG, Color: "text-blue-600 dark:text-blue-400"}))
Example
package main

import (
	"github.com/larsartmann/templ-components/htmx"
)

func main() {
	_ = htmx.LoadingIndicator(nil)
}

func PolledLiveIsValid

func PolledLiveIsValid(v PolledLive) bool

PolledLiveIsValid reports whether v is one of the defined PolledLive constants.

func PolledRegion

func PolledRegion(props PolledRegionProps) templ.Component

PolledRegion renders a div that auto-refreshes its content via HTMX polling on an interval. The region replaces itself (hx-swap="outerHTML") on each poll, which means the children re-render server-side on every request.

Include a "Updated HH:MM:SS" timestamp (ShowTimestamp: true) so operators can verify polling is active — if the timestamp freezes, polling has stalled.

When Eager is set, the region renders with an aria-busy="true" busy cue (data-tc-polled-busy marker) and a singleton script that fires the first fetch once per initial-DOM region on DOMContentLoaded, then clears the busy cue on the first completed request (htmx:afterSwap covers successful self-replacement, htmx:afterRequest covers error completions) — parity with datastar.LiveRegion's busy cue, so screen readers wait for the fresh content instead of announcing the stale initial render twice.

EAGER NEVER EMITS AN hx-trigger "load" TOKEN. With the default hx-swap="outerHTML" the region replaces ITSELF on every response; htmx processes each swapped-in element and fires "load" again, so hx-trigger="load, every Ns" on a self-replacing element is an infinite self-refetch loop (the 2026-09-04 DiscordSync projection-health outage). The eager fetch is therefore issued by the script as a one-shot htmx.ajax call. Regions injected after the initial page load (htmx partial swaps, boosted navigation) get no eager fetch and degrade to interval-only polling — the busy cue clears on their first interval response.

@htmx.PolledRegion(htmx.PolledRegionProps{URL: "/partials/stats", Every: "10s", Eager: true}) {
   @display.StatCard(display.StatCardProps{Label: "Messages", Value: "1,234"})
}

HYBRID RENDERING CAVEAT (strings.Builder path): PolledRegion is a children-slot component — rendered outside a templ file (component.Render straight into a strings.Builder) the { children... } slot renders EMPTY, and an empty polled region swaps itself for the endpoint's response on the first tick anyway. Render PolledRegion from a real templ file whose children are the server-side partial (the poll endpoint should serve that same partial), or pass children programmatically via templ.WithChildren(ctx, child) before Render. See docs/recipes/hybrid-strings-builder-rendering.md.

Example
package main

import (
	"github.com/larsartmann/templ-components/htmx"
)

func main() {
	_ = htmx.PolledRegion(htmx.DefaultPolledRegionProps())
}

func SwapOOB

func SwapOOB(props SwapOOBProps) templ.Component

SwapOOB renders an out-of-band swap wrapper for HTMX responses. Use this to update multiple elements in a single response. Invalid swap styles fall back to outerHTML.

@htmx.SwapOOB(htmx.SwapOOBProps{Selector: "#toast-container", SwapStyle: htmx.SwapBeforeEnd}) {
   @feedback.Toast(props)
 }
Example
package main

import (
	"github.com/larsartmann/templ-components/htmx"
)

func main() {
	_ = htmx.SwapOOB(htmx.SwapOOBProps{Selector: "#list"})
}

func SwapStyleIsValid

func SwapStyleIsValid(v SwapStyle) bool

SwapStyleIsValid reports whether v is one of the defined SwapStyle constants.

func ViewTransitions

func ViewTransitions(props ViewTransitionsProps) templ.Component

ViewTransitions renders a CSP-safe script that enables native View Transitions for HTMX swaps, plus default transition CSS.

Place this component once per page, typically in the <head> or at the end of <body> before other HTMX-using components.

@htmx.ViewTransitions(htmx.ViewTransitionsProps{Global: true})
Example
package main

import (
	"github.com/larsartmann/templ-components/htmx"
)

func main() {
	_ = htmx.ViewTransitions(htmx.DefaultViewTransitionsProps())
}

Types

type ConfirmDeleteProps

type ConfirmDeleteProps struct {
	utils.BaseProps
	Delete  string
	Target  string
	Confirm string
}

ConfirmDeleteProps configures a confirmation delete button.

type ErrorHandlingConfig

type ErrorHandlingConfig struct {
	// Nonce is the CSP nonce for inline scripts
	Nonce string
	// MaxErrorHistory is the maximum number of errors to track (default: 10)
	MaxErrorHistory int
	// MaxRetries is the maximum number of retry attempts for server errors (default: 2)
	MaxRetries int
	// RetryDelayMS is the base delay in milliseconds between retries, multiplied by retry count (default: 1000)
	RetryDelayMS int
}

ErrorHandlingConfig configures the global HTMX error handling behavior

func DefaultErrorHandlingConfig

func DefaultErrorHandlingConfig() ErrorHandlingConfig

DefaultErrorHandlingConfig returns sensible defaults for error handling

type PolledLive

type PolledLive string

PolledLive is the aria-live politeness setting for a polled region.

const (
	// PolledLivePolite announces updates after the current speech finishes (default).
	PolledLivePolite PolledLive = "polite"
	// PolledLiveAssertive announces updates immediately, interrupting current speech.
	PolledLiveAssertive PolledLive = "assertive"
	// PolledLiveOff disables screen-reader announcements for this region.
	PolledLiveOff PolledLive = "off"
)

type PolledRegionProps

type PolledRegionProps struct {
	utils.BaseProps

	// URL is the HTMX endpoint to fetch for refreshing the region content.
	URL string

	// Every is the poll interval as an HTMX timing string (e.g. "10s", "2m", "500ms").
	Every string

	// Eager fires the first fetch immediately on page load in addition to
	// polling. When false, the first fetch happens after the first interval
	// elapses. Ignored when Trigger is set.
	//
	// Eager never emits an hx-trigger "load" token: with the default
	// hx-swap="outerHTML" the region replaces itself on every response and
	// htmx re-fires "load" for each swapped-in element, which is an
	// infinite self-refetch loop. Instead the busy-cue script issues one
	// htmx.ajax call per initial-DOM region on DOMContentLoaded. Regions
	// injected after the initial page load degrade to interval-only
	// polling.
	Eager bool

	// Trigger overrides the auto-generated hx-trigger value. When set,
	// Every and Eager are ignored and this string is used verbatim as
	// hx-trigger. Useful for custom triggers like SSE events
	// (e.g. "stats-refresh from:body").
	Trigger string

	// Swap controls how the fetched content replaces the region.
	// Defaults to SwapOuterHTML.
	Swap SwapStyle

	// Live sets the aria-live politeness for screen-reader announcements.
	// Defaults to PolledLivePolite.
	Live PolledLive

	// ShowTimestamp renders a "Updated HH:MM:SS" footer so operators can see
	// at a glance that polling is active (the timestamp ticks forward on each
	// successful poll because the whole region re-renders).
	ShowTimestamp bool

	// Now overrides the wall clock used for the timestamp footer. Nil (the
	// zero default) means time.Now. Inject a fixed clock in visual goldens
	// and tests so ShowTimestamp coverage is deterministic instead of
	// flaking across the second boundary (or being disabled outright).
	Now func() time.Time

	// TimeFormat is the Go time format string for the timestamp footer.
	// Default: "15:04:05" (time-only). Use time.RFC3339 for full date+time.
	TimeFormat string
}

PolledRegionProps configures an auto-refreshing HTMX region.

func DefaultPolledRegionProps

func DefaultPolledRegionProps() PolledRegionProps

DefaultPolledRegionProps returns sensible defaults for a polled region.

type SwapOOBProps

type SwapOOBProps struct {
	utils.BaseProps
	Selector  string
	SwapStyle SwapStyle
}

SwapOOBProps configures an out-of-band swap wrapper.

type SwapStyle

type SwapStyle string

SwapStyle is an HTMX swap style for out-of-band updates.

const (
	SwapInnerHTML   SwapStyle = "innerHTML"
	SwapOuterHTML   SwapStyle = "outerHTML"
	SwapBeforeBegin SwapStyle = "beforebegin"
	SwapAfterBegin  SwapStyle = "afterbegin"
	SwapBeforeEnd   SwapStyle = "beforeend"
	SwapAfterEnd    SwapStyle = "afterend"
	SwapDelete      SwapStyle = "delete"
	SwapNone        SwapStyle = "none"
)

HTMX swap styles (https://htmx.org/attributes/hx-swap/).

type ViewTransitionsProps

type ViewTransitionsProps struct {
	utils.BaseProps

	// Global enables View Transitions for ALL HTMX swaps on the page.
	// When false, consumers opt in per-element via hx-swap="... transition:true".
	Global bool
}

ViewTransitionsProps configures native View Transitions API integration for HTMX content swaps.

The View Transitions API provides smooth, animated transitions between DOM states — cross-fades, shared-element morphing, and custom animations — with zero JavaScript beyond enabling the feature.

HTMX 2.0 supports View Transitions natively. This component:

  • Optionally enables View Transitions globally for all HTMX swaps
  • Renders CSS for default and customizable transition animations

Graceful degradation: browsers without View Transitions support (older Firefox/Safari) perform instant swaps — identical to current behavior.

func DefaultViewTransitionsProps

func DefaultViewTransitionsProps() ViewTransitionsProps

DefaultViewTransitionsProps returns sensible defaults.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL