{"id":36511185,"url":"https://github.com/stackus/hxgo","last_synced_at":"2026-01-14T15:55:12.289Z","repository":{"id":211778064,"uuid":"729361821","full_name":"stackus/hxgo","owner":"stackus","description":"HTMX request and response helpers for Go","archived":false,"fork":false,"pushed_at":"2023-12-11T19:30:53.000Z","size":168,"stargazers_count":24,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-12T05:06:23.145Z","etag":null,"topics":["golang","htmx"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stackus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-12-09T02:08:46.000Z","updated_at":"2025-09-27T21:23:56.000Z","dependencies_parsed_at":"2023-12-10T20:43:52.611Z","dependency_job_id":null,"html_url":"https://github.com/stackus/hxgo","commit_stats":null,"previous_names":["stackus/htmx","stackus/hxgo"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/stackus/hxgo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackus%2Fhxgo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackus%2Fhxgo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackus%2Fhxgo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackus%2Fhxgo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stackus","download_url":"https://codeload.github.com/stackus/hxgo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackus%2Fhxgo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28425569,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T15:24:48.085Z","status":"ssl_error","status_checked_at":"2026-01-14T15:23:41.940Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","htmx"],"created_at":"2026-01-12T02:35:52.008Z","updated_at":"2026-01-14T15:55:12.284Z","avatar_url":"https://github.com/stackus.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"![golangish-htmxish-logo.png](golangish-htmxish-logo.png)\n\n# HTMX library for Go\n[![GoDoc](https://godoc.org/github.com/stackus/hxgo?status.svg)](https://godoc.org/github.com/stackus/hxgo)\n\nThis comprehensive library offers an array of functions and types specifically designed to streamline the handling of [HTMX](https://htmx.org/) requests and the construction of responses in the Go applications.\n\n\u003e README.md logo image courtesy of ChatGPT.\n\n## Features\n- Request and Response header helpers\n- Easy APIs to build complex HTMX responses for Locations, Reswaps, and Triggers\n\n```go\nimport (\n    \"net/http\"\n    \n    \"github.com/stackus/hxgo\"\n)\n\nfunc myHandler(w http.ResponseWriter, r *http.Request) {\n    if hx.IsHtmx(r) {\n        // do something\n\t\t\n        // load up on HTMX headers and set the status code to send back to the client\n        err := hx.Response(w,\n            hx.Location(\"/new-location\",\n                hx.Target(\"#my-target\"),\n                hx.Swap(hx.SwapInnerHtml.IgnoreTitle()),\n                hx.Values(map[string]string{\"key\": \"value\"}),\n            ),\n            hx.StatusStopPolling,\n            hx.Trigger(\n                hx.Event(\"my-event\"),\n                hx.Event(\"my-other-event\", \"my-other-event-value\"),\n                hx.Event(\"my-complex-event\", map[string]any{\n                    \"foo\": \"bar\",\n                    \"baz\": 123,\n                }\n            ),\n        )\n        if err != nil {\n            // handle error\n        }\n    }\n}\n```\n\n## Installation\nThe minimum version of Go required is **1.18**. Generics have been used to make some types and options easier to work with.\n\nInstall using `go get`:\n```bash\ngo get github.com/stackus/hxgo\n```\n\nThen import the package into your project:\n```go\nimport \"github.com/stackus/hxgo\"\n```\n\nYou'll then use `hx.*` to access the functions and types.\n\n## Working with Requests\nTo determine if a request is an HTMX request, use the `IsHtmx` function:\n\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    if hx.IsHtmx(r) {\n        // do something\n    }\n}\n```\n\nHelpers exist for each of the [HTMX request headers](https://htmx.org/reference/#request_headers):\n\n- `HX-Boosted`: Use the `IsBoosted` function to determine if the request is a boosted request\n- `HX-Current-URL`: Use the `GetCurrentUrl` function to get the current URL of the request\n- `HX-History-Restore-Request`: Use the `IsHistoryRestoreRequest` function to determine if the request is a history restore request\n- `HX-Prompt`: Use the `GetPrompt` function to get the prompt value of the request\n- `HX-Request`: Use the `IsRequest` or `IsHTMX` functions to determine if the request is an HTMX request\n- `HX-Target`: Use the `GetTarget` function to get the target value of the request\n- `HX-Trigger-Name`: Use the `GetTriggerName` function to get the trigger name of the request\n- `HX-Trigger`: Use the `GetTrigger` function to get the trigger value of the request\n\n`Is*` functions return a boolean while `Get*` functions return a string. The absence of the corresponding HTMX header will return false or an empty string respectively.\n\n## Working with Responses\nUse the `Response` function to modify the `http.ResponseWriter` to return an HTMX response:\n\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    err := hx.Response(w, hx.Retarget(\"/new-location\"))\n\tif err != nil {\n        // handle error\n    }\n}\n```\n\nEach of the [HTMX response headers](https://htmx.org/reference/#response_headers) has a corresponding option to set the header:\n\n- `HX-Location`: Use the `Location` option with a variable number of properties to set the location header. See the [Location](#location) section for more details.\n- `HX-Push-Url`: Use the `PushURL` option to push a new URL into the browser history\n- `HX-Redirect`: Use the `Redirect` option to redirect the browser to a new URL\n- `HX-Refresh`: Use the `Refresh` option to refresh the browser\n- `HX-Replace-Url`: Use the `ReplaceUrl` option to replace the current URL in the browser history\n- `HX-Reswap`: Use the `Reswap` option or one of the `Swap*` constants to specify how the response will be swapped. See the [Reswap](#reswap) section for more details.\n- `HX-Retarget`: Use the `Retarget` option with a CSS selector to redirect the response to a new element\n- `HX-Reselect`: Use the `Reselect` option with a CSS selector to designate a different element in the response to be used\n- `HX-Trigger`: Use the `Trigger` option to trigger client-side events. See the [Trigger](#trigger) section for more details.\n- `HX-Trigger-After-Settle`: Use the `TriggerAfterSettle` option to trigger client-side events after the response has settled. See the [Trigger](#trigger) section for more details.\n- `HX-Trigger-After-Swap`: Use the `TriggerAfterSwap` option to trigger client-side events after the response has been swapped. See the [Trigger](#trigger) section for more details.\n\n### Location\nThe `Location` option is used to set the [HX-Location Response Header](https://htmx.org/headers/hx-location/). It takes a path string and then an optional number of properties. The following properties are supported:\n\n- `Source`: The `Source` property is used to set the source element of the location header.\n- `Event`: The `EventName` property is used to set the name of the event of the location header.  \n  \u003e Note: This property is called `EventName` so that it does not conflict with the `Event` property used by the `Trigger` option.\n- `Handler`: The `Handler` property is used to set the handler of the location header.\n- `Target`: The `Target` property is used to set the target of the location header.\n- `Swap`: The `Swap` property is used to set the swap of the location header. The value may be a string or any of the `Swap*` constants.\n- `Values`: The `Values` property is used to set the values of the location header. The value may be anything, but it is recommended to use a `map[string]any` or struct with JSON tags.\n- `Headers`: The `Headers` property is used to set the headers of the location header. The value needs to be a `map[string]string`.\n- `Select`: The `Select` property is used to set the select of the location header.\n\nSetting just the path:\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    hx.Response(w, hx.Location(\"/new-location\"))\n\t  // Hx-Location: /new-location\n}\n```\nSetting multiple properties:\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    hx.Response(w, hx.Location(\"/new-location\",\n        hx.Target(\"#my-target\"),\n        hx.Swap(hx.SwapInnerHtml.IgnoreTitle()),\n        hx.Values(map[string]string{\"key\": \"value\"}),\n    ))\n    // Hx-Location: {\"path\":\"/new-location\",\"target\":\"#my-target\",\"swap\":\"innerHTML ignoreTitle:true\",\"values\":{\"key\":\"value\"}}\n}\n```\n\n### Reswap\nThe `Reswap` option is used to set the HX-Reswap response header. Using the `Reswap` option directly is possible, but it is recommended to use one of the `Swap*` constants instead. The following constants are supported:\n\n- `SwapInnerHtml`: Sets the HX-Reswap response header to `innerHTML`\n- `SwapOuterHtml`: Sets the HX-Reswap response header to `outerHTML`\n- `SwapBeforeBegin`: Sets the HX-Reswap response header to `beforebegin`\n- `SwapAfterBegin`: Sets the HX-Reswap response header to `afterbegin`\n- `SwapBeforeEnd`: Sets the HX-Reswap response header to `beforeend`\n- `SwapAfterEnd`: Sets the HX-Reswap response header to `afterend`\n- `SwapDelete`: Sets the HX-Reswap response header to `delete`\n- `SwapNone`: Sets the HX-Reswap response header to `none`\n\nThe result from `Reswap` and each constant can be chained with modifiers to configure the header even further. The following modifiers are supported:\n\n- `Transition`: Adds `transition:true` to enable the use of the View Transition API\n- `Swap`: Used with a `time.Duration` to set the swap delay\n- `Settle`: Used with a `time.Duration` to set the settle delay\n- `IgnoreTitle`: Adds `ignoreTitle:true` to ignore the title of the response\n- `Scroll`: Used with a CSS selector to scroll to the element after swapping\n- `Show`: Used with a CSS selector to show the element after swapping\n- `FocusScroll`: Used with a boolean to set the focus scroll behavior\n\nSetting just the reswap header two ways:\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    hx.Response(w, hx.Reswap(\"innerHTML\"))\n    // Hx-Reswap: innerHTML\n    hx.Response(w, hx.SwapInnerHtml)\n    // Hx-Reswap: innerHTML\n}\n```\n\nSetting the reswap header with modifiers:\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    hx.Response(w, hx.SwapInnerHtml.IgnoreTitle().Transition())\n    // Hx-Reswap: innerHTML ignoreTitle:true transition:true\n}\n```\n\n### Trigger\nThe `Trigger` option is used to set the [HX-Trigger Response Header](https://htmx.org/headers/hx-trigger/). It takes a variable number of events to trigger on the client.\n\nEvents are created using `hx.Event` and can be either simple names or complex objects. The supported events include:\n\nSetting a simple event:\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    hx.Response(w, hx.Trigger(hx.Event(\"my-event\")))\n    // Hx-Trigger: {\"my-event\":null}\n}\n```\n\nSetting a complex event:\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    myEvent := map[string]any{\n        \"foo\": \"bar\",\n        \"baz\": 123,\n    }\n\n    hx.Response(w, hx.Trigger(hx.Event(\"my-event\", myEvent)))\n\t  // Hx-Trigger: {\"my-event\":{\"foo\":\"bar\",\"baz\":123}}\n}\n```\n\nSetting multiple events:\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    hx.Response(w, hx.Trigger(\n        hx.Event(\"my-event\"),\n        hx.Event(\"my-other-event\", \"my-other-event-value\"),\n    ))\n    // Hx-Trigger: {\"my-event\":null,\"my-other-event\":\"my-other-event-value\"}\n}\n```\n\nThe `data`, which is the second parameter of the `Event`, is variadic. If more than one data value is passed, the event is set to an array of those values. The following events demonstrate this equivalence:\n\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    hx.Response(w, hx.Trigger(\n        hx.Event(\"my-event-1\", \"foo\", \"bar\"),\n        hx.Event(\"my-event-2\", []string{\"foo\", \"bar\"}),\n    ))\n    // Hx-Trigger: {\"my-event-1\":[\"foo\",\"bar\"], \"my-event-2\":[\"foo\",\"bar\"]}\n}\n```\n\nBoth `TriggerAfterSettle` and `TriggerAfterSwap` are available to trigger events after the response has settled or been swapped respectively. They take the same event arguments as `Trigger`.\n\n### Status\nThe `Status` option is used to set the HTTP status code of the response. There is only one status constant available:\n\n- `StatusStopPolling`: Sets the HTTP status code to 286 which is used by HTMX to halt polling requests\n\nSetting the status code:\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    hx.Response(w, hx.StatusStopPolling)\n    // HTTP/1.1 286\n}\n```\n\nThe `Status` option can be used to set any HTTP status code and is not limited to the constants provided by this library.\n\n```go\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    hx.Response(w, hx.Status(http.StatusGone))\n    // HTTP/1.1 410\n}\n```\n\n## Usage with different HTTP frameworks\nWith the standard library, and other frameworks that adhere to its `http.ResponseWriter` interface, the `Response` function can be used directly to modify the response.\n\n### Standard Library (and some like Chi)\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/stackus/hxgo\"\n)\n\nfunc helloWorldHandler(w http.ResponseWriter, r *http.Request) {\n\t// Add HTMX headers and a status code to the response\n\terr := hx.Response(w,\n\t\thx.Location(\"/foo\"),\n\t\thx.StatusStopPolling,\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Write the response body\n\t_, _ = fmt.Fprintf(w, \"Hello World\")\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/\", helloWorldHandler)\n\n\tfmt.Println(\"Server starting on port 8080...\")\n\tif err := http.ListenAndServe(\":8080\", nil); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n### Echo, Fiber, and other frameworks\nFor frameworks that do not use the standard library's `http.ResponseWriter` interface, there are request and response helpers available to make it easier to work with HTMX.\n\nFor example, with [Echo](https://echo.labstack.com/):\n\n```go\npackage main\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\n\t\"github.com/stackus/hxgo\"\n\t\"github.com/stackus/hxgo/hxecho\"\n)\n\nfunc main() {\n\t// Create a new instance of Echo\n\te := echo.New()\n\n\t// Define a route for \"/\"\n\te.GET(\"/\", func(c echo.Context) error {\n\t\t// use hxecho.IsHtmx to determine if the request is an HTMX request\n\t\tif hxecho.IsHtmx(c) {\n\t\t\t// do something\n\t\t\t// Adds HTMX headers but does not set the Status Code\n\t\t\tr, err := hxecho.Response(c,\n\t\t\t\t// Continue to use the base htmx types and options\n\t\t\t\thx.Location(\"/foo\"),\n\t\t\t\thx.StatusStopPolling,\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// Set the HTMX status code here and response body\n\t\t\treturn c.String(r.StatusCode(), \"Hello Echo\")\n\t\t}\n\t})\n\n\t// Start the server on port 8080\n\te.Logger.Fatal(e.Start(\":8080\"))\n}\n```\n\nYou will find request and response helpers for the following frameworks:\n- Echo: [hxecho](./hxecho)\n- Fiber: [hxfiber](./hxfiber)\n- Gin: [hxgin](./hxgin)\n\nThe `Response` function for each library will return a default status of 200 if no status is set.\nIf you need to set a status code, you can use the `Status` option.\n\n### Contributions\nContributions are welcome! Please open an issue or submit a pull request. If at all possible, please provide an example with your bug reports and tests with your pull requests.\n\n#### Reporting Bugs\n- If you find a bug, please open an issue.\n- Include a clear description of the bug, steps to reproduce it, and any relevant logs or screenshots.\n- Before creating a new issue, please check if it has already been reported to avoid duplicates.\n\n#### Suggesting Enhancements\n- We're always looking to improve our library. If you have ideas for new features or enhancements, feel free to open an issue to discuss it.\n- Clearly explain your suggestion and its potential benefits.\n\n### License\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackus%2Fhxgo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstackus%2Fhxgo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackus%2Fhxgo/lists"}