Giter VIP home page Giter VIP logo

htmx-go's Introduction

GoDoc GitHub Workflow Status (with event) License Stars Discord

htmx-go is a type-safe library for working with HTMX in Go.

Less time fiddling with HTTP headers, more time developing awesome Hypermedia-driven applications.

Check if requests are from HTMX, and use a type-safe, declarative syntax for HTMX response headers to control HTMX behavior from the server.

Write triggers without dealing with JSON formatting. Define trigger behavior, and htmx-go handles the rest.

Use Swap Strategy methods to fine-tune hx-swap behavior.

Uses standard net/http types. Has basic integration with templ components.

import (
	"net/http"

	"github.com/angelofallars/htmx-go"
)

func handler(w http.ResponseWriter, r *http.Request) {
	if htmx.IsHTMX(r) {
		htmx.NewResponse().
			Reswap(htmx.SwapBeforeEnd).
			Retarget("#errors").
			ReplaceURL("/errors").
			Write(w)
	}
}

Think this project is awesome? Consider sponsoring me ๐Ÿ’™

Installation

Use go get.

go get github.com/angelofallars/htmx-go

Then import htmx-go:

import "github.com/angelofallars/htmx-go"

HTMX Requests

Check request origin

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 page load on your website, as initial page load requests don't use HTMX.

func handler(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)
	}
}

Check if request is Boosted (hx-boost)

func handler(w http.ResponseWriter, r *http.Request) {
	if htmx.IsBoosted(r) {
		// logic for handling boosted requests
	} else {
		// logic for handling non-boosted requests
	}
}

HTMX responses

htmx-go takes inspiration from Lip Gloss for a declarative way of specifying HTMX response headers.

Basic usage

Make a response writer with htmx.NewResponse(), and add a header to it to make the page refresh:

func handler(w http.ResponseWriter, r *http.Request) {
	writer := htmx.NewResponse().Refresh(true)
	writer.Write(w)
}

Retarget response to a different element

func handler(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)
}

Triggers

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 either map[string]string or structs with JSON field tags.

Set trigger headers using the preceding triggers:

  • Response.AddTrigger(trigger ...EventTrigger) - appends to the HX-Trigger header
  • Response.AddTriggerAfterSettle(trigger ...EventTrigger) - appends to the HX-Trigger-After-Settle header
  • Response.AddTriggerAfterSwap(trigger ...EventTrigger) - appends to the HX-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"}}

Swap strategy

Response.Reswap() takes in SwapStrategy values from this library.

htmx.NewResponse().
	Reswap(htmx.SwapInnerHTML)
// HX-Reswap: innerHTML

htmx.NewResponse().
	Reswap(htmx.SwapAfterEnd.Transition(true))
// HX-Reswap: innerHTML transition:true

Exported SwapStrategy constant values can be appended with modifiers through their methods. If successive methods write to the same modifier, the modifier is always replaced with the latest one.

import "time"

htmx.SwapInnerHTMl.After(time.Second * 1)
// HX-Reswap: innerHTML swap:1s

htmx.SwapBeforeEnd.Scroll(htmx.Bottom)
// HX-Reswap: beforeend scroll:bottom

htmx.SwapAfterEnd.IgnoreTitle(true)
// HX-Reswap: afterend ignoreTitle:true

htmx.SwapAfterEnd.FocusScroll(true)
// HX-Reswap: afterend ignoreTitle:true

htmx.SwapInnerHTML.ShowOn("#another-div", htmx.Top)
// HX-Reswap: innerHTML show:#another-div:top

// Modifier chaining
htmx.SwapInnerHTML.ShowOn("#another-div", htmx.Top).After(time.Millisecond * 500)
// HX-Reswap: innerHTML show:#another-div:top swap:500ms

htmx.SwapBeforeBegin.ShowWindow(htmx.Top)
// HX-Reswap: beforebegin show:window:top

htmx.SwapDefault.ShowNone()
// HX-Reswap: show:none

HTMX Reference: hx-swap

Code organization

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)
}

Templ integration

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's recommended to use Response().StatusCode() so the status code header is always set after the HTMX headers.

Stop polling

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)

Header names

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"
)

Compatibility

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.

If you use Fiber, it is recommended to use htmx-fiber instead, which is a fork of htmx-go.

Additional resources

Contributing

Pull requests are welcome!

License

MIT

htmx-go's People

Contributors

angelofallars avatar julianooi avatar rafiramadhana avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.