Getting started
From install to a rendered fragment in five steps. (This walkthrough is compiled and run by the repository's test suite โ it cannot rot.)
1. Install
go install github.com/go-monolith/ghtmx/cmd/ghtmx@latest
go install golang.org/x/tools/gopls@latest # embedded-Go support in the LSP
No Go toolchain, or you would rather take the release binary? On Linux,
macOS, and WSL, scripts/install.sh downloads it, checks it against
checksums.txt, and adds gopls when go is available:
curl -fsSL https://raw.githubusercontent.com/go-monolith/ghtmx/main/scripts/install.sh | bash
On VS Code that one line is also the editor setup: the script finds the
code CLI, and if the ghtmx extension is missing it asks before
installing the .vsix from the same release.
Install the ghtmx VS Code extension (syntax highlighting, diagnostics, completion)? [y/N]
Nothing is installed unless you answer y, and a run with no terminal
to ask on skips the question. --no-interactive turns off every prompt,
GHTMX_INSTALL_VSCODE=1 answers yes in advance, and
GHTMX_SKIP_VSCODE=1 leaves the editor alone.
2. Create a module and pin htmx
mkdir hello && cd hello
go mod init example.com/hello
Create ghtmx.json โ the htmx version the project runs on. It decides
both the script HTMXScript() serves and the syntax the compiler
checks, so the markup you validate and the htmx the browser runs cannot
disagree:
{
"htmxVersion": "4.0.0"
}
3. Write the app
Create main.go โ routes are ordinary net/http registrations; the
route discoverer reads them at build time:
package main
import (
"log"
"net/http"
)
func Index(w http.ResponseWriter, r *http.Request) {
if err := page("world").Render(r.Context(), w); err != nil {
log.Printf("render: %v", err)
}
}
func Greeting(w http.ResponseWriter, r *http.Request) {
if err := greetingFragment("htmx").RenderFragment(r.Context(), w); err != nil {
log.Printf("render: %v", err)
}
}
func routes() *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("GET /{$}", Index)
mux.HandleFunc("GET /greeting", Greeting)
return mux
}
func main() {
log.Println("listening on :8080")
log.Fatal(http.ListenAndServe(":8080", routes()))
}
Create page.ghtmx โ the button binds the Greeting handler by
symbol (the compiler folds its route path in statically), and the
greeting is a fragment with both an inline and a standalone entry
point:
package main
import "example.com/hello/ghtmxgen"
templ page(name string) {
<!DOCTYPE html>
<html>
<head>
@ghtmxgen.HTMXScript()
</head>
<body>
<button hx-get={ Greeting } hx-target="#greeting" hx-swap="outerHTML">Greet</button>
@greeting(name)
</body>
</html>
}
fragment greeting(name string) {
<p id="greeting">Hello, { name }!</p>
}
4. Generate and tidy
ghtmx generate
go mod tidy
Generation writes page_ghtmx.go beside the template and the central
ghtmxgen package (HTMXScript(), which serves htmx 4.0.0 with its
integrity hash, and โ for parameterised routes โ typed URL
constructors). If you rename Greeting later, both the registration
and the binding break the build: that is the point.
Two expected notices on this first run: a version-check note about
ghtmx not being in go.mod yet (the tidy that follows fixes it), and
GHTMX-W0104 for the GET / page route, which is navigated to rather
than bound โ full-page routes always warn like this.
Without a ghtmx.json the project is pinned to htmx 2.0.10 instead โ
any 2.0.x release works, and the compiler then checks htmx 2 syntax.
See htmx versions for what differs between the
two and how to move a project across; the htmx4-* entries under
Examples are complete htmx 4 applications.
5. Run it โ and render a fragment
go run .
A browser on http://localhost:8080 shows the page; clicking
Greet swaps in the fragment. The same standalone fragment renders
directly:
curl -H "HX-Request: true" http://localhost:8080/greeting
<p id="greeting">Hello, htmx!</p>
That response is byte-identical to the fragment embedded in the full page โ the compile-time fragment guarantee.
Where next
ghtmx generate -watch -cmd 'go run .' -proxy http://localhost:8080gives live reload with a dev proxy.- The Syntax specification covers every construct;
examples/crudin the repository is the full reference application with events, typed constructors, and inline editing.