One way β not the way β to build full-stack Go apps with HTMX + Alpine.js.
A batteries-included starter framework that wires together Chi, Templ, HTMX 2, and Alpine.js into a coherent project structure. SQLite out of the box, Postgres/MySQL with one env-var change.
Note: Chi was chosen out of personal interest, not because it is the only option. The standard library alone can take you very far β here is a great talk showing exactly that.
| Package | What it does |
|---|---|
htmx/ |
Inspect every HTMX request header; set every response header (HX-Redirect, HX-Trigger, HX-Reswapβ¦) |
flash/ |
Stateless HMAC-signed cookie flash messages β no session store needed, survive HTMX redirects |
validation/ |
Explicit form validation helpers (Required, MinLen, IsEmail, Matches, Unique) |
middleware/ |
JWT stored in an HttpOnly cookie β Protect and Optional route guards, UserFromCtx helper |
db/ |
database/sql connection + embedded SQL migration runner (auto-runs on startup, no CLI needed) |
db/models/ |
User CRUD: GetByID, GetByUsername, GetByEmail, Create, Update, Delete |
services/ |
Auth service (register / login / logout with bcrypt + JWT) and a base web-handler helper |
views/ |
Templ layouts and pages wired to Tailwind CDN β swap in a build step when you're ready |
cmd/
main.go β entry point: connect DB, run migrations, start server
seed/main.go β optional database seeder
config/ β env-based config loader
db/
migrations/ β SQL migrations (auto-applied on startup)
models/ β database model types + queries
htmx/ β HTMX request/response helpers
flash/ β cookie flash message helpers
middleware/ β JWT auth middleware
routes/ β route registration
services/ β business logic
utils/ β shared utilities
validation/ β form validation
views/
layouts/ β base HTML layout, nav, flash
pages/ β home, login, register
static/ β embedded static assets (HTMX, Alpine, Bootstrap JS)
# 1. Clone
git clone https://github.com/livghit/go-htmx && cd go-htmx
# 2. Configure
cp example.env app.env
# Open app.env and set JWT_SECRET and FLASH_SECRET to long random strings:
# openssl rand -hex 32
# 3. Run (with hot-reload)
make dev
# 4. Or build a binary
make build && ./bin/appThe server starts on :3000. The SQLite database is created automatically and migrations are applied on startup.
| Target | Description |
|---|---|
make dev |
Hot-reload via air |
make run |
Run without hot-reload |
make build |
Compile production binary to bin/app |
make templ |
Regenerate Go code from .templ files |
make migrate-up |
Apply pending migrations via goose CLI |
make migrate-down |
Roll back the latest migration |
make seed |
Seed the database |
make lint |
Run golangci-lint |
make test |
Run all tests |
make clean |
Remove bin/ and app.db |
Copy example.env to app.env (never commit app.env).
APP_NAME="MyApp"
PORT=":3000"
DB_ENGINE=sqlite # sqlite | postgres | mysql
DB_NAME=app.db
JWT_SECRET=change-me # openssl rand -hex 32
JWT_EXPIRY=24 # hours
FLASH_SECRET=change-me # openssl rand -hex 32To use Postgres, set:
DB_ENGINE=postgres
DB_CONN=postgres://user:pass@localhost:5432/myapp?sslmode=disableOpen a pull request or an issue β see Contributing.md for details.
