There is a very intense backlash against technology like React, due to the unbelievable complexity that has crept into building “modern” JS apps.
This backlash is quite understandable. I believe Ember/React/Vue/Angular/etc. were all created with good intentions, but with the ecosystems of bolted-on technologies and now-necessary build tools, they have all become unmanageable monstrosities.
So people want to go “back to basics”. Personally, while I loved Ember with CoffeeScript (talking to a Django server via JSON) in 2015-2017, I can’t even revive the app I built without a massive effort to upgrade and ditch all the deprecated, unsupported dependencies (which might take more time than modifying my actual app code).
So I’m just going to rewrite the Web client with htmx (with Django rendering the actual templates). No churn and dependency hell, no build tools, no nonsense. Just a single script tag and some HTML attributes. For special cases that can’t be covered by htmx, there’s hyperscript (just add another script tag). Since browsers intend to maintain backward compatibility indefinitely, forced upgrades should be minimal to non-existent.
It’s a shame JS ended up being perceived as a villain, because it was meant to enable rich client applications within browsers, which are the most universally-compatible and commodified client you can possibly run such apps in.
For those who are primarily javascript developers who want to go back to the basics, Astro.build is very nice. Ships plain HTML pages by default. Easy to progressively enhance with client-side interactivity if and where you'd like.
And for the client-side stuff, you get to pick how to tackle it: plain javascript, htmx, web components, react, vue, svelte, qwik... all enjoy first class support.
Astro needs a better elevator pitch. I spent several minutes on the site, clicking and scrolling, but sample code was minimal and not easy to find. This is not effective marketing in 2023. If a developer has no idea what developing with your tech stack feels like after ~60 seconds, more times than not that developer will leave. Sorry, Astro sounds cool conceptually, but in this case I think brutally honest feedback is warranted.
For what it’s worth, I also think Django needs a better elevator pitch, but Django captured me in 2010. I might try pushing for a home page improvement. Django does have tenure and mindshare working in its favor, though.
Recently I've been drifting away from template engines toward native data structures + serialization for programmatically creating html/yaml. For example, CDK is following this pattern for building cloud configuration YAML.
Do folks know of standard libraries or patterns in Go or Rust to generate HTML without string templating? (As mentioned elsewhere, I'm looking for a backend for HTMX)
The thing about these is that performance is often not as good as when using templates, especially when the templates are compiled to native code. Quicktemplate [1] is still the leader here IMO, and I don't think the OP project brings much that couldn't be done pretty easily with QT.
gomponents is amazing! I find the abstractions just right. it now looks possible that I can port my minimalistic bun/deno setup with htmx to go.
I found "gow" watcher that allows auto-restart of the development webserver when go code was changed. And I'm using "hx-refresh" header paramter and "hx-trigger='every 250ms'" to have a simple polling browser refresh.
it also integrates well with tailwind css. you can use tailwind vscode extension and define a custom "classRegex" for gomponents "Class(...)" function.
I needed to write a custom `Classes()` function that allows me to have conditional classes like `clsx()`. it's a bit hacky, but I like the simplicity.
func Classes(nodes ...g.Node) g.Node {
var classes []string
for _, node := range nodes {
if node != nil {
var builder strings.Builder
_ = node.Render(&builder)
c := builder.String()
c = strings.TrimLeft(c, " class=\"")
c = strings.TrimRight(c, "\"")
classes = append(classes, c)
}
}
return Class(strings.Join(classes, " "))
}
the runtime panics for functions that use vararg for optional arguments and have an invalid number of arguments is probably an okayish trade-off. mixing attributes and children elements in the builder functions, I don't know if it's good or bad, but I like the simplicity; JSX and hyperscript-like functions separate tag, attributes, and children into three different parameters.
Yeah, I'm not a total fan of the runtime panics, but it was a tradeoff where I chose in favour of API simplicity and readability. In practice, it works out well, because you catch any typos at development time.
I've had elements and attributes in separate packages before, but then you can't dot-import both packages and not have name clashes, and have to either prefix elements or attributes with the package name, which makes the code much less readable. Again, a tradeoff. :)
Oh, thank you for open sourcing it! I'm having a lot of fun, and it's nearly as agile as react, only hit by the lack of expressiveness in the go language in some cases.
I'm always open for feedback, so if you think there's room for improvement, feel free to open an issue for discussion on the repo. (That said, I'm fairly conservative about the API, trying to keep it simple for everyone to use.)
Even in React we do a lot of programmatic “UI generation” for data structures, and mostly use react for the more “unique” things, which is arguably a big part of our Enterprise application. We do use shared components, but it’s also very nice not to share them too widely in terms of testability and maintenance.
So I think it’s perfectly reasonable not to use tempting if it works for you. Especially if the templating isn’t very good, which is frankly most of them. I do think some templating can be good for prototyping, but as soon as you’re doing the same thing a lot of times and that thing doesn’t really change beyond what happens to the underlaying data structure, I think it’s very worth it to build it programmatically. Which is basically a lot of data CRUD UI in many organisations.
It is refreshingly different from other Rust templating libraries. It uses a proc-macro that compiles your HTML into Rust code. I also happen to use it in conjunction with HTMX and it works very well for me (at least in small projects).
You will still have to learn a little bit of "special" syntax though, which you are looking to avoid if I understand you correctly.
I believe I recently built something to do exactly what you're describing for HTML, though it's C++ (but maybe a useful basis for a rust version - I don't know).
I was initially skeptical, because I feel like the built-in HTML templating is really powerful and good. Recently however, I listened to an episode of the "Go Time" podcast[1] featuring the author and he made some very compelling cases for it.
The most compelling to me is that Templ templates are compile time safe, rather than crashing at run time like the built-in templates.
Part of a long line of similar projects: XHP, Pyxl, JSX. My own project Scalatags is pretty popular in the Scala community.
The approach works surprisingly well, both server side and running in the browser (JSX, or Scalatags+Scala.js). There are plenty of websites that don't need heavy client-side interactivity where server side templating + a sprinkling of client side logic works just fine. This is especially true if you can share code between client and server (e.g. via JSX/React or Scalatags/Scala.js)
You get typechecking, interop with normal code, and a high performance runtime for free. That's a lot to like!
For java/Kotlin, we are using JTE (https://jte.gg/#getting-started), pretty nice build tooling and IDE support to generate pre-compiled templates without additional code generation steps.
While this is incredible and I can't wait to use this with HTMX, I really wish it didn't require a CLI to generate the *_templ.go files.
Code generation is something I saw in Flutter/Dart a lot and it entirely killed my interest in it (mostly with JSON serialization/deserialization to classes).
I use Taskfile, fd (a 'find' alternative) and "entr" to get around any need to manually call 'templ generate'.
'fd . 'assets/view' | entr templ generate' - couple it with 'air' and developing is a lot easier. Looking forward to when Goland has better support for templ.
It’s very different. It’s a meta language that compiles to Go. It ensures correct syntax at compile time, and makes it much easier to treat parts of views as real values instead of bits of text. Much better composition, at the cost of a much more complex build
system.
I think this answered my only question: how the heck is he just writing HTML inside of Go, as far as I know, Go doesn't have any metaprogramming to that extent, and I doubt Go ever will.
Last I checked Gos built in templating system lacked one feature that I see as very basic and that IMO makes it hard to organize code and templates logically:
To very common things to do when templating is
- to include a sub-template (e.g. the article template includes the sidebar template)
- to specify that the template I work with and probably others should be rendered inside a slot in a containing template (e.g. the article template should be rendered into the general root template that contains the public menu but the admin pages should render in another root template)
Last I checked only the first of these use cases were supported.
You're right. But this only means that it won't be possible that you use the same template for different slots. Personally, I don't remember I ever needed that. Actually, most template engines I know work this way (https://twig.symfony.com/doc/2.x/templates.html#template-inh..., "{% block content %}").
But still, it's easy to archieve that by just creating another sub template.
So your first template file is the layout, the second one contains the main block definition, and that definition you just include another sub template which can be reused elsewhere.
That’s what I like about Go. The platform is prescriptive, but in all the right places. For things like this, Go leaves the abstraction building to you, if you need it.
The standard templating in Go has been good enough for my small projects, and I usually avoid adding libraries if I can help it. But seeing this makes me want to try it just to see how it compares.
I'm in a similar position. I dismissed Templ by default when it started getting mentioned; I've always disliked JSX and the approach reminds me of that. I realized that it basically enables compiler and type checking of the template code though and now I'm interested. Ie, with html/template, you could easily reference a variable/attribute/etc that doesn't exist and have no way of knowing until you hit it at runtime so you had to be careful to write tests that covered all the conditional paths in the templates.
there's an obvious typo of a variable name in the template "Titl" instead of "Title". Program compiles. Parse() succeeds. It just fails at runtime (outputting "<title>" and stopping. My understanding is that a Templ equivalent would probably end up with a compile error much earlier on (and probably more obvious in your IDE as well).
Yes, it's exactly like having a bug in your code. Using Templ means that it gets caught at compile time (if not earlier since your IDE can show you that there's a problem as soon as you write the code). That's (IMHO) almost always preferable to catching it at test time.
Standard Go templating seems really lacking if you come from something like Jinja. Even with libraries like https://masterminds.github.io/sprig/ (used e.g. for Helm templating) it feels hard to use.
Standard Go templating can do almost anything you want it to do, but figuring out how to do it from the documentation alone can be a real challenge.
If this sounds to the reader like a defense of Go templates... it isn't.
Plus, I would agree that while it can do almost anything you want, that doesn't mean it always does it in a good way, or in a way that makes sense if you aren't a programmer. It's a pity. There's some power there, and I like the attempt at making templating safe, but it could really use a v2.
Nice! I like how it can double as a static site generator, so you can replace Hugo/Jekyll with this. That might need some glue code/extensions to the language though.
That's what I did for my website. Combined with goldmark for writing my pages in markdown, I really like it.
It did require a fair amount of code though.
My problem with Hugo is how the templating becomes very confusing and the flow convoluted. The projects required places for various things becomes a maze, especially if you are not familiar with the site and theme you are working on.
I definitely prefer JSX to Hugo, but this project might be a nice middle ground.
I've got my own super simple service template that I use internally for this: https://github.com/maragudk/service . I don't think I've added HTMX here (because I add it when needed), but it has auth and is totally classic in its setup.
Well, just use cookies. It's simple as that.
As markusw, I also don't use HTMX. I tried it, but for me it is faster to use the fetch API. My basic stack is go html templates, chi router and sqlite.
From my brief usage it seems to lack a couple of basic stuff, but nothing that couldn't be added later on.
But, I am curious why they decided to go with reserving keywords (like if and for) instead of doing what JSX does and just wrapping any Go code in braces.
To be honest, I tried working with it and it was really confusing. I'd just rather keep writing out the HTML I want to return and keep my applications simple and straightforward.
The same is still possible in PHP to include HTML code into you PHP files. But it's rarely still being done like this.
It's considered good practise to separate your PHP (business) and HTML (view) logic.
It's 2023 and there is jinja, liquid, moustache and then some. Nothing extraordinary. Nothing truly cross language.
I think that if the dice fell just slightly different, PHP could have taken that niche. Be the best templating language that has bindings to all major languages.
It was really good at arrays, but then piled objects, hashmaps and more on top of these arrays. It had very good and broad string manipulation std functions. But the it piled SQL on top of that.
Imagine if instead of mysql_real_escape it had focused on (nested) caching. Or if instead of the umpteenth mediocre OOP class inheritance it introduced template partials as objects to be defined. If, instead of clumsy form handling, it had the world's fasted serialization (to xml, html, JSON etc). Instead of csv parsers, it offered the best stdlib for rendering data. And so on.
It has some nice properties: streams by default, it’s pretty fast for a templating language and you get a lot of stuff for free that you eventually need.
However, you have to escape and sanitize output explicitly, which is not a thing in pretty much any modern templating language.
The std lib in go for example has a very explicit security model.
The problem is that you have to escape differently depending on the micro context. So it can become tedious and error prone if you have to do it explicitly all the time.
> However, you have to escape and sanitize output explicitly, which is not a thing in pretty much any modern templating language.
Idk if it has to with "modern" or not. Proper escaping contexts (whether, and which delimiters to quote in attributes, in content, in markup declarations, ...) are already defined by SGML, with HTML-aware templating or other vocabulary-specific expansion such as suppressing <script> elements injection as would be required by basic user commenting also being covered by SGML. My impression was that go/templating (and maybe pug?), while considered advanced relative to other templating packages shitting strings into markup, still requires explicit sanitation depending on the insertion context.
"A HTML templating language for Go that has great developer tooling" as long as you're using VSCode... I don't see a JetBrains (GoLand/IntelliJ) plugin.
I'd have to ask him if it's finished, but Joe, one of the core contributors to Templ definitely has a jetbrains plugin in the works: https://github.com/joerdav/templ-jetbrains
That's nice. I'm a heavy user of go html templating and it gets tiring after a while - it has no type checking, methods and attributes are called the same way, calling Parse is awkward, adding Sprig is pretty much required, the order you call functions is lisp-like, it's all weird.
Currently switching from Echo/Jet to Echo/Templ - compilation (generation) is a little bit annoying but writing simpler Go code in templates makes a difference, also easier to compose.
I find myself thinking this as well. I love the idea of this for a few reasons, but there appear to be some hang ups that would cause me to go back to typescript, jsx, and a node process serving it.
The developer experience is ridiculously nice in that ecosystem if you’re accustomed to it. So many QOL things have been dealt with and polished.
But I do love the idea of something like that dev experience with Go instead of JavaScript powering it.
Maybe this would be totally sufficient if you don’t want any SPA/interactivity features?
Some sort of live pipe to component state like LiveView or similar could make it so a lot of JS would work implicitly, which might give a lot of the dev experience and user experience ease that people want. I’m not sure. But I do think something would often be missing with this library.
Regardless, I love the idea and I’d be stoked to see it grow and improve the developer experience.
With every project that grew the need for UI state I either wished I went with client side rendering or did so from the start for the parts that needed it.
The issue is: manipulating the DOM can become hard to reason about. You either somehow manage to use the DOM as your source of truth, or you manually sync your state into and out of the DOM, and other things like the URL etc.
The “immediate mode” style GUI model is much easier to reason about, because you can think of each render as a fresh start.
You don’t need React for this, in fact other libraries do this faster and without additional complexity such as Lit. Plus if you use something with web components, it is easier to mix and match client side and server side templating.
Certainly. I never meant to say "my javascript strategy is X and its the best fit everywhere'. As always "it depends".
Though, the simple idea of server-side rendered HTML that is enhanced with JS goes a very long way.
> or you manually sync your state into and out of the DOM, and other things like the URL etc.
The thing is: with server-side-rendered HTML, the issue of "State" is solved! Hypermedia is a state-machine (REST, HATEOAS etc). What is left, is "state" needed for rendering some variations.
You don't need to store state of routers, or the state of JSON objects being pushed through immutable-redux-saga-whatevers. All you need is some state to determine "the accordeon is closed" or "the dropdown is open".
I've applied my strategy to a client-side search system once. And while it worked, it quickly showed that actual state-management was needed. I used the URL/history-api to store all state - so that links where sharable, but there's only so much you can put into paths, query strings and anchors. I used HTML5 template tags for templates and components and some simple JS to fetch JSON from a search-backend and pull that through the template to insert into the DOM.
What I'm saying is: yes! I agree. You'll need proper and nice design patters or architectures to build a UI of any scale. But I'm also saying: if you move state-management to REST (the backend, server-side-rendered) what you are left with is simple enough that it needs no framework most of the time.
Yeah, I mean stuff you can't or won't put into the client-server communication. At some level of detail it becomes wasteful.
For example a multi-step form, where you can browse for options, have a lot of filters such as logic that displays different options based on previously selected ones and so on.
It's not that you can't do it via HTTP. But it's also wasteful. For this kind of stuff you don't need to pay for the round-trips. The server doesn't need to know anything about the UI state here. The only transactional parts are the entry point and the final result.
Things like these are a great place for "interactive islands". You can architect the skeleton and all the actually transactional parts as a normal HTTP/REST site, that exchanges HTML and so on. But for the parts that are really _just_ UI related state like the above, it's unnecessary work (compute/complexity/networking) to do so. Other examples include: interactive visualizations via SVG/WebGL, rich text editing and so on. All of those things are relatively common things.
What I was getting at above, is that it will get messy if you have JS state like that and but you don't write it in a way where the JS "owns" the DOM, from start to finish, for that component or island. It doesn't have to be react/redux or w/e. There are simpler, more performant solutions that lean on web standards and are much lighter weight like Lit etc.
> The server doesn't need to know anything about the UI state here.
As always: "it depends". I've built many use-cases where the server did really need to know about this. When we needed the intermediate state to be stored so that a user could pick up where she left. Or when we needed the telemetry/events of intermediate states being sent (e.g. "your ticket is now reserved for 45 minutes please proceed to payment") and so on.
What's more, and more important though, is that no:
> But it's also wasteful.
It is hardly wasteful. Hell, your "ga.js" HTTP call is probably heavier than doing a simple call to proceed the state-machine over HTTP. HTTP certainly isn't the problem: most likely the choice of a bulky and sluggish backend language/framework is, though.
I think JS is fine for some UI sugar, but the app should be usable even with JS off. What you are talking about is indeed better accomplished with an SPA.
While "it must be usable with JS off" was true decades ago for accessability and compatibility reasons, this isn't the case anymore.
In my case, the reason for changing and not creating the DOM with JS is pure simplicity. I'll just as gladly make something that won't work, because it cannot be changed this way. Because everyone except for a very few "mormons of the internet" have JS. Modern JS even.
I agree. I use server-side rendering heavily in my projects at work and thus largely avoid custom Javascript (edit: the reason is laziness, not because I outright disliked Javascript). But if I want to enable some very basic user interactivity that does not alter "state" in any way, I will always go for a few lines of JS that e.g. toggles the visibility of some items when a button is clicked etc.
So (as usual) the key is to find the balance that works for you instead of pursuing one way (only server-side rendering or only SPA Javascript framework).
For the time being this approach works very well for me.
This backlash is quite understandable. I believe Ember/React/Vue/Angular/etc. were all created with good intentions, but with the ecosystems of bolted-on technologies and now-necessary build tools, they have all become unmanageable monstrosities.
So people want to go “back to basics”. Personally, while I loved Ember with CoffeeScript (talking to a Django server via JSON) in 2015-2017, I can’t even revive the app I built without a massive effort to upgrade and ditch all the deprecated, unsupported dependencies (which might take more time than modifying my actual app code).
So I’m just going to rewrite the Web client with htmx (with Django rendering the actual templates). No churn and dependency hell, no build tools, no nonsense. Just a single script tag and some HTML attributes. For special cases that can’t be covered by htmx, there’s hyperscript (just add another script tag). Since browsers intend to maintain backward compatibility indefinitely, forced upgrades should be minimal to non-existent.
It’s a shame JS ended up being perceived as a villain, because it was meant to enable rich client applications within browsers, which are the most universally-compatible and commodified client you can possibly run such apps in.