javascript-today

Why Hugo + htmlx + Go is the most underrated stack in 2026!

There’s a certain kind of relief in building something without a node_modules folder that outweighs the project itself. Hugo, htmx, and Go together deliver that relief and then keep going — you get a genuinely fast static site, a real way to add dynamic behavior without adopting a frontend framework, and a deployment story that’s embarrassingly simple compared to almost anything else on the market. This stack doesn’t get the conference-talk hype that Astro or Next.js get, but it quietly does more with less than most of what’s fashionable right now.

Here’s the actual case for it.


The pitch in one sentence

Build your site as static HTML with Hugo, add interactivity only where you need it with htmx, and back the dynamic bits with a small Go server — then ship the whole thing as a single binary.

Each piece is doing the job it’s actually good at, and none of them is doing a job it’s bad at.


Hugo: the fastest thing in this comparison, full stop

Hugo is written in Go and compiles your entire site to static HTML at build time. There’s no runtime, no client-side framework, no JavaScript unless you add it yourself. On large content sites — hundreds or thousands of pages — Hugo’s rebuild times are measured in milliseconds where Node-based generators are measured in seconds.

The part that matters for this stack specifically: Hugo has a module.mounts config option that lets you serve arbitrary directories as content. That’s the mechanism that makes the htmx integration below work — you mount a partials directory and now any file in it is fetchable as an HTML fragment:

[module]
  [[module.mounts]]
  source = 'partials'
  target = 'content'

No plugin, no special islands syntax. Just Hugo doing what it already does — serving files — pointed at a directory you control.


htmx: interactivity without a client framework

htmx is a ~14KB library that lets HTML attributes trigger AJAX requests and swap the response directly into the page. No JSON, no client-side state management, no build step. The server renders HTML; htmx just moves pieces of it around.

A button that loads content on click looks like this — no JavaScript file, no bundler, just attributes:

<div
  hx-get="/content.html"
  hx-trigger="click"
  hx-target="#content_target">
  <button>Load content</button>
  <div id="content_target"></div>
</div>

A form that posts to a real backend and swaps in the response:

<form hx-post="{{ .Site.Params.apiBaseUrl }}/hello_world_form">
  <label>Name</label>
  <input type="text" name="name">
  <button>Submit</button>
</form>

That’s a working contact form, comment box, or vote counter with zero lines of custom JavaScript. For the class of interactions that make up most real sites — forms, filters, pagination, “load more,” simple CRUD — this is genuinely less code and less to reason about than reaching for a component framework.


Go: the backend that turns “static” into “static, plus”

The Go half handles whatever Hugo can’t — processing form submissions, rendering dynamic fragments, talking to a database. A minimal handler for the form above is exactly what you’d expect from net/http:

func helloWorldForm(w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	name := r.FormValue("name")
	partials.HelloWorldGreeting(name).Render(r.Context(), w)
}

You can render with the standard library’s html/template, or with templ if you want type-checked Go templates instead of string-based ones. Either way, there’s no separate rendering runtime to learn — it’s the same Go you’d write for any other backend.

The deployment story is the real payoff. The hugo-htmx-go-template starter project (a solid reference implementation of this whole pattern) ships tooling that compiles your entire Hugo site and your Go API server into one binary:

bin/build
GOOS=openbsd GOARCH=amd64 bin/build   # cross-compile for any target

The output is a single file. Copy it to a server, run it, done. No Node runtime to install, no separate static file host to configure, no Docker multi-stage build to get right. If you’ve ever had to explain to someone why your “simple static site” needs an npm install step, a CDN, and a separate API deployment pipeline, the contrast here is stark.


What you actually get

  • Build speed that stays fast as content scales, because Hugo doesn’t slow down the way JS-based generators tend to
  • Zero JS by default, with interactivity added deliberately and only where needed, using a model (server renders HTML, client just swaps it in) that’s easier to reason about than client-side state management for most CRUD-shaped features
  • One deployable artifact. A single Go binary with your whole site and API embedded is about as close to “it just works” as web deployment gets in 2026
  • No framework churn risk. Hugo, htmx, and Go all move slowly and deliberately by design — you’re not exposed to a frontend framework’s major-version treadmill

Honest caveats

This isn’t a universal answer, and it’s worth being straight about where it stops working:

  • It’s not free of a backend anymore. The moment you add htmx-powered forms or dynamic fragments, you’ve introduced a real server process with its own uptime, scaling, and deployment concerns — you’ve traded “purely static, host anywhere for free” for “static plus a small service to run.” That’s a fair trade for a lot of projects, but it’s a real one.
  • It doesn’t fit apps that need fine-grained client state. Rich text editors, drag-and-drop interfaces, real-time collaborative tools — anything where a network round trip per interaction would be genuinely too slow — still wants a real component framework. htmx is honest about this; it’s not trying to be React.
  • Smaller ecosystem than the mainstream options. Fewer tutorials, fewer Stack Overflow answers, fewer pre-built integrations than you’d get with Next.js or even Astro. You’ll be reading source code and writing your own glue more often.
  • It’s a pattern, not a polished product. hugo-htmx-go-template is a solid starting point, but you’re assembling three separate tools yourself rather than adopting one cohesive framework with a single opinionated way of doing things. That’s a feature for engineers who want control, and a liability for teams that want guardrails.

When to actually reach for this

This stack earns its keep on content-heavy sites — blogs, documentation, marketing sites, personal sites — that need a handful of genuinely dynamic features: a contact form, a comment system, a simple vote counter, a newsletter signup that doesn’t redirect to a third-party page. If that’s the shape of the project, Hugo plus htmx plus Go gets you there with less infrastructure, less JavaScript, and a simpler deployment than almost anything else being recommended in 2026.

If the project is actually an application — persistent client state, real-time updates, complex interactive widgets — this isn’t the stack, and pretending otherwise just means fighting the tools instead of using them. But for the enormous number of sites that are “mostly static with a few dynamic bits,” this combination does more with less than its lack of hype would suggest.