The Go Web Stack That Actually Makes Sense in 2025

2 Feb 2025

Battery included frameworks like Laravel, Rails and Next.js have a place in enterprise teams and startups, but the golang developer really has no direct competitor. I’ve landed on what I consider to be the building blocks for a perfect Golang web development stack. My choices optimize for delivery speed, simplicity and fun.

Here’s my battle-tested combo:

  • Templ for templating
  • Chi for routing and http middleware
  • Air for smooth dev ex.

Why templ is a Game-Changer

Sure, Go has built-in templating, but Templ takes it to another level. You get strong typing and a language server for great dev experience. Here’s a quick taste.

package contact

import (
   "github.com/ebuckley/mtb/components/layout"
   "github.com/ebuckley/mtb/lib/site"
)

templ Thanks(sp site.Site) {
   @layout.Page(sp, ContactMeta(sp), nil, site.Contact) {
      <h1 class="font-sans underline font-bold break-normal text-xl md:text-3xl">Thanks for getting in touch!</h1>
      <p\>I'll get back to you as soon as I can.</p>
   }
}

The beauty here is that a template looks a lot like a go code file. You can use strongly typed parameters, and get nice code completion for the fields that are passed in. Embedding sub templates is easy using as I am here where I reference my page layout template layout.Page.

Templ is a code generator so you compile the .templ files to plain old go functions. This means super fast performance in production and zero framework overhead.

Single Binary deployments

Deploying a go web application just means putting a binary up on a server and keeping it running. This is all thanks to the standard library embed package. Two lines is all it takes:

//go:embed public
var staticFiles embed.FS

The above takes my public folder and embeds it all in the binary. You access the files using the standard file system interface. Code which works with the FS or io interfaces will ‘just work ™‘ with an embedded file same as a real file.

My http middleware setup can use a folder or an embedded FS as simple as this:

lib.FileServer(mux, "/", http.FS(staticFS))

This little middleware will take any path not served by an existing route and look up a public file from the embed.

The caveat is that you need to always reload to re-embed. I have a solution for you though. All you need is Air!

Supercharge Development with Air

For golang developers, air is a secret weapon. It handles hot reloading so you can just hit save then refresh the browser. There is one gotcha; you need to configure around an infinite loop between air + templ.

To reload my app I use

cmd = "bunx tailwindcss -i styles/input.css -o cmd/server/public/output.css && templ generate && go build -o ./tmp/server ./cmd/server"

The compile command will compile tailwind css output and templ functions. This means that if you do not exclude certain folders and file patterns then an infinite loop of compilation and generation might happen.

To work around it use the following bit of config in your .air.toml

exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules", "cmd/server/public"]
exclude_regex = ["_test.go", ".*_templ.go"]

We ignore big folders like testdata and node_modules and places where the content changes as a result of the build I.E cmd/server/public

We also ignore _templ.go files because they will be generated from our previous templ generate step during compilation.

Choose simplicity

Look, there are a million ways to build web apps. But after trying pretty much everything out there, this stack just hits different. Simplicity and power that works perfect for me.

The best part about all this is that it starts small and can scale when you need it. No Over-engineering, no framework lock-in. Just standard library and small choices that can be replaced if needed.

If you’re starting with Go for web development in 2025, give this mix of libraries a shot. And hey, if you’ve got questions or want to share your own Go setup, @ me on the socials or contact form – I love talking shop.

Happy Coding!

Subscribe to my Newsletter

Want to know more? Put your email in the box for updates on my blog posts and projects. Emails sent a maximum of twice a month.