Getting Started

Everything you need to build your first GO(A)HT app.

GO(A)HT = Go + HTMX + Tailwind, with Alpine.js in parentheses for latency-sensitive islands. GO(A)HT stays HTML-first: the server renders pages, HTMX handles partial updates, SSE handles push-driven refreshes, and Alpine is reserved for local state that needs to respond before the server round-trip.

The recommendation order is:

  1. Full-page HTML
  2. HTMX partials
  3. SSE
  4. Alpine, if necessary

If a screen is just CRUD, navigation, or read-heavy content, stop at HTML or HTMX. If updates need to fan out to multiple clients, add SSE. If you need optimistic updates or complex local coordination, add a small Alpine island and give it JSON endpoints. Otherwise, stay in HTML.

Why GO(A)HT?

  • No bundler. Your JavaScript is two <script> tags.
  • No framework. Go’s stdlib handles routing, templates, and HTTP.
  • No hydration. The server returns HTML. The browser renders it.
  • One binary. go build → deploy. That’s it.

Prerequisites

  • Go 1.22+ (for enhanced ServeMux routing)
  • PostgreSQL (for data)
  • Redis (optional, for SSE/pub-sub)
  • Node.js (only for Tailwind CLI and Playwright tests)

Create a Project

mkdir myapp && cd myapp
go mod init github.com/you/myapp

Create the standard directory structure:

myapp/
├── cmd/
│   └── server/
│       └── main.go          # Entry point
├── internal/
│   ├── handler/              # HTTP handlers
│   ├── service/              # Business logic
│   ├── middleware/            # Auth, logging
│   └── model/                # sqlc generated code
├── templates/
│   ├── layouts/              # Base layouts
│   ├── pages/                # Full page templates
│   └── partials/             # Reusable fragments
├── styles/
│   └── main.css              # Tailwind entry point
├── static/                   # Built CSS, images
├── model/
│   ├── migrations/           # SQL migrations
│   ├── queries.sql           # sqlc queries
│   └── sqlc.yaml             # sqlc config
└── go.mod

Minimal Server

package main

import (
    "log"
    "net/http"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("GET /{$}", handleHome)

    log.Println("listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", mux))
}

That’s a minimal HTTP server example. No framework. No dependencies. Read on to learn each piece of GO(A)HT.