Show HN: htmgo - build simple and scalable systems with golang + htmx (htmgo.dev)
Hey all, I just wanted to share a project I've been working on for the past month.
After years of heavy frameworks, I really like the idea of using htmx, but it’s a little too low level for me and needs a thin layer above it to facilitate things like components, better syntax with complex JS inside of an attribute, etc
To try and solve this problem with a very minimal stack (golang + htmx) that I've been really enjoying, I'm building this project to cater to my needs and was thinking it would be useful for other developers.
85 comments
[ 1089 ms ] story [ 3998 ms ] thread1. A development experience with instant visual feedback for the frontend. Even more so when using tailwind.
2. A stable, safe and performant backend.
The downside is that a node process has to be running and doing the ssr, though that is an okay trade off for me because my project is mainly the api, having a reference implementation of the frontend is just a nice extra.
I've also experimented with implementing the reverse proxy in rust itself and using a unix socket and other IPC mechanisms to push the data to the SSR layer.
I think this most people would call this backend-, api- or service development. Especially if your api endpoint does not do anything web specific except http.
Anyway, how do you share types between the backend and frontend? Are you rawdogging json?
I use the ts-rs crate, which adds a derive(TS) macro for types in rust and generates typescript types. It works so far but my side projects are only medium sized. But I'm sure there are other options too.
Which I have also been starting to use for one project, with quarkus, been a nice experience so far.
I hear you on point #2 though. The Red Hat IntelliJ plugin for that seems janky to me. And JetBrains own plugin doesn’t seem great either. In fact I don’t know if I’ve ever seen autocomplete in a Qute template ever.
Shame, because the rest of the stack is so damn good.
No I mean, I can tell it will get slow, haha.
> the rest of the stack is so damn good.
I agree! (so far, I think)
edit: typo
[0] https://github.com/canopyclimate/golive
return h.NewPage( h.Div( h.Class("flex gap-2"), h.TextF("the current time is %s", now.String()) ) )
To me that is horrible.
With it properly spaced out and nested, after a few days it started reading exactly like HTML to me, where I can quickly see the hiearchy
It doesn't look too bad if one also break the code into multiple functions to make "layouts" and "components".
I have had lots of fun building with Bun, ElysiaJS, and HTMX. Might test your go library out as well. Looks pretty neat.
Prejudice may not be in social areas but the same behaviour of prejudice is alive and well in justifying your own or denigrating someone else’s technology choices.
Same folks aren’t willing to be open to what is new to them in a new framework is still a 20 year old thing.
Having an open mind, and curious mind that seeks out new ways and experiences not something that happens randomly. You have to cultivate it in yourself and others.
Leading with it sets context before the fact.
I suppose bike shedding still matters to people who see themselves as Hemingway not an engineer
> I suppose bike shedding still matters to people who see themselves as Hemingway not an engineer
This is anything but bikeshedding.
I’m mean fine have fun with your libraries but the amount of excitement for something like this? Amazing.
Don't get me wrong, I love HTMX, I just don't want to write DSL to generate HTML. IMO a backend language should integrate HTMX similar to how https://hotwired.dev/ works.
Turbo stimulus and Strada..
HTMX... Nothing. Just serve it however you feel. In the posts case, they decided to add some tight coupling with go, while not for everyone, is not really a framework.
I appreciate go server for really lightweight banal sites or even light services and htmlx is quickly becoming my new favourite markup. I will say this project isn't for me, but casting it as just another 'fasthtml' project is a bit far off the mark.
HTMX for example requires that a successful form submission respond with a 200 status, but many applications (most?) will 302 redirect to a new page to prevent duplicate form request on refresh.
This is not a criticism or a metaphor, just an observation of their technical similarities. May I ask if there are any aspects I might be unaware of that make you feel this comparison is inappropriate?
I switched from using golang templates with hand-crafted HTML to gomponents[1] a few months ago, and it's been amazing. It is so so so much easier to write re-usable components, so much easier to express complicated conditionals, so much nicer to have a type checker helping you not make mistakes.
And of course I like gomponents for the same reason (I presume) JS-oriented people like NodeJS: It's just a lot nicer to have as much as possible written in the same language, be it JavaScript or Golang.
[1] https://www.gomponents.com/
What do you think about https://templ.guide/? To me, the ability to still write raw HTML in Go is incomparable.
I dream of a library like ours to take on the likes of React, and to get there the devex needs to have some key features. Most notably, imo, is live reload. You could use air, but I find it still to be too slow to recompile the entire app. I have had some success so far with yaegi to interpret the go at runtime [2]. It isn't perfect, but the full language spec is implemented.
My personal goal is to build the Go equivalent of rails/django. Live reloading is needed in addition to plugins that provide web app primitives (auth, storage, logging, metrics, etc). Additionally, I think the network effect of React is a powerful value driver, so some easy way to include React in an app is also important. Thankfully evanw has made this trivial with esbuild [3]
[1] https://github.com/breadchris/share/blob/master/html2/html.g... [2] https://github.com/traefik/yaegi [3] https://github.com/breadchris/share/blob/master/graph/build....
> You could use air, but I find it still to be too slow to recompile the entire app.
At the moment I'm using fsnotify to watch file changes and restart the process immediately, so far it hasn't been too bad for live reloading. I'm hoping as long as precautions are taken to lazy load things on startup, then it would stay fairly quick.
Maybe you want to have a look at the code and get some inspiration.
https://github.com/taoeffect/dragonfly-newlisp
If you want to collab more, shoot me an email: chris@breadchris.com
[1] https://github.com/DCjanus/yaegi_demo
I'm really curious to do some benchmarking and see how this compares to template-based HTML generation. It seems theoretically possible to accumulate raw data first and then build the document from top to bottom (instead of piecing together a lot of intermediate strings), but I'm not sure the complexity would justify the performance gains.
I also just learned about `gocomponents` [2] in another thread here, which seems to be a similar idea with a substantial headstart. I could probably just switch to it, but I kind of like the idea of following a pattern instead of installing a dependency.
[1] https://gist.github.com/allknowingfrog/951fbaa221a3a504b382f... [2] https://github.com/maragudk/gomponents
[1] https://templ.guide/
There were a few typos in the docs page: https://triplechecker.com/s/D32t6y/htmgo.dev?v=HrUfl
The routing uses the std lib + chi, it's fairly integrated into the html builder because the http request needs to be utilized to check specific htmx headers.
I could imagine you could use your own web framework though, since you can wrap the std lib handlers.
Hand-coding HTML is three decades in, like using computer languages from the 1970s in the 2000s.
There are so many of these experiments the last few years, could any be what replaces HTML5?
If not, why not? What would be enough better? Most likely not just another form of the same.
Here is an "Elm-like" example from https://elmprogramming.com/building-a-simple-page-in-elm.htm... coded in raku. HTMX is a great lever to put the code on the server - thus Go, Raku, whatever.
From the module synopsis which means some of the capabilities of the Raku Quoting Slang are in play ... https://raku.land/zef:librasteve/HTML::Functional. Raku functions only need parens for disambiguation btw. Named attrs can use the :name<value> syntax which imo is the tidiest.[1] https://www.gomponents.com/