I love Go. It's simple, fast, and the type system saves me from 90% of the bugs I used to write in dynamic languages.
But whenever I wanted to build a full-stack web app, I felt like I had to leave that simplicity behind. The industry standard pushed me towards:
- Building a REST/GraphQL API in Go.
- Building a completely separate SPA in React/Next.js.
- Managing state, hydration, JWTs, CORS, and npm install hell.
Suddenly, 80% of my time was spent on "glue code" and configuration, not on my product.
I just wanted to ship.
So, I decided to go against the grain. I built a production-ready SaaS using the GOTH stack: Go, Others (HTMX), Templ, and Htmx.
Here is why this stack is a game-changer for Go developers, and how I structured it.
The Stack 🛠️
- Backend: Go (Standard Lib + Chi Router).
- Templating: Templ (Type-safe HTML generation).
- Interactivity: HTMX (Server-driven UI updates).
- Database: PostgreSQL + pgx (No heavy ORMs).
Why Templ? (The Secret Weapon)
If you've used html/template, you know it's... okay. But it's string-based. You don't know if you broke your UI until you run the app.
Templ compiles your HTML into Go code. This means:
- Type Safety: If you pass the wrong data to a component, your code won't compile.
- Performance: It compiles to efficient Go byte buffer writes. It's blazingly fast.
- DX: You get autocomplete for your HTML components in VS Code.
Here is a simple example of a reusable Button component in Templ:
Go
package components
import "gosaaskit/types"
templ Button(props types.ButtonProps) {
<button
class={ "px-4 py-2 rounded", props.Class }
hx-post={ props.HXPost }
hx-target={ props.HXTarget }
hx-swap="outerHTML"
>
{ props.Label }
</button>
}
The "No-API" Architecture
With HTMX, you don't need to build a JSON API and a separate frontend to consume it. Your Go handler returns HTML fragments.
Here is how a "Click to Edit" user profile flow looks:
- The Handler (Go): Instead of returning json.NewEncoder(w).Encode(user), we render the component.
Go
func (h *UserHandler) UpdateProfile(w http.ResponseWriter, r *http.Request) {
// 1. Parse form
name := r.FormValue("name")
// 2. Update DB
user, _ := h.db.UpdateUser(name)
// 3. Render ONLY the updated component (not the whole page)
components.UserProfile(user).Render(r.Context(), w)
}
- The UI (HTMX): The form submits via AJAX, and the response replaces the form with the updated view.
HTML
<form hx-put="/user/profile" hx-target="this" hx-swap="outerHTML">
<input type="text" name="name" value="John Doe" />
<button type="submit">Save</button>
</form>
Zero JSON. Zero client-side state management. Zero hydration errors.
The Benefits for Indie Hackers 🚀
- Single Binary Deployment: The whole app (frontend + backend) compiles into one binary. Put it in a Docker scratch container (15MB) and deploy anywhere.
- SEO Ready: It's Server-Side Rendered (SSR) by default. Google loves it.
- Speed: No massive JS bundles to download. The Time-To-Interactive is instant.
The Hard Part (and the Solution)
While the stack is amazing, setting up the "boring stuff" takes time:
- Configuring Docker & Hot Reload (Air).
- Setting up Authentication (Login, Register, Password Reset, Email magic links).
- Integrating Stripe/Lemon Squeezy for subscriptions.
- Building UI components (Modals, Toasts, Tables).
I spent about 200 hours building this foundation for my own projects.
I realized other Go developers probably want to skip this setup too. So, I packaged it all into a production-ready starter kit.
It's called Go SaaS Kit. It includes:
- ✅ Full Auth system (cookie-based sessions).
- ✅ Stripe & Lemon Squeezy integration.
- ✅ PostgreSQL + pgx migrations.
- ✅ beautifully designed UI components (Tailwind).
- ✅ Docker ready.
If you want to save weeks of boilerplate work and start writing your business logic today, check it out.
(PS: Use code GOSAASKIT20 for 20% off for the community)
Let's Discuss! 💬
Have you tried HTMX with Go? Do you prefer it over the React/Next.js ecosystem? Let me know in the comments!
Top comments (0)