Hacker News new | past | comments | ask | show | jobs | submit login
Templ: A language for writing HTML user interfaces in Go (github.com/a-h)
228 points by nalgeon 5 months ago | hide | past | favorite | 98 comments



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)


Here are a couple:

https://github.com/julvo/htmlgo

https://github.com/rohanthewiz/element

I'm sure there are many more.

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.

[1] https://github.com/valyala/quicktemplate


You can try gomponents https://www.gomponents.com/

It also has an HTMX plug in.


Thank you for the mention! :)

Relevant libraries, for those who want to dive directly into it:

- https://github.com/maragudk/gomponents

- https://github.com/maragudk/gomponents-htmx


I tried it yesterday until far past midnight.

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.

edit: reformat and typos.


I'm really happy you like it. :) And thank you for your comment, I really appreciate newcomer viewpoints especially.

Have a look at the `Classes` helper component already available: https://github.com/maragudk/gomponents/blob/main/components/...

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.


You're welcome! :)

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.)


i just wish gomponents/html would define an alias for gomponents.Text so i don't have the impedance mismatch of switching the package qualifier

but otherwise, I'm quite liking it so far


Hmm. I see what you mean, but <text> isn't a valid HTML element, so that wouldn't work for that alone.

But try dot imports for the html package and you don't have to have a package qualifier for most of your view code at all! :D


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.


I would like to mention maud in this context:

https://github.com/lambda-fairy/maud

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.


while it looks nice, I’d prefer html and not to learn yet another DSL, the reason is that migrating is easier between template engines.


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).

https://github.com/rthrfrd/webxx


I don't really understand what you're looking for and I don't write web apps in Go, so sorry if this is a wrong answer.

The templ language linked here is compiling something that looks similar to React's JSX into Go code. And then you can do HTMX: https://templ.guide/server-side-rendering/htmx/


If you want to really go back to oldskool way you could use XML for this.


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.

1. https://changelog.com/gotime/291


Seeing a lot of people use this as part of the GoTH stack: go, temple & htmx


>GoTH stack

LAMP, MEAN, JAM and now GoTH...damn! web people sure have good taste in naming things!


I'm not sure how to react to this vue, opinions are quite angular in frontend and it almost seems required to flutter about the various frameworks


CHAD stack is still my favorite.


That was my first thought. It looks perfect for use with HTMX.


Love the name. :D


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.


JTE was a like refinding sanity after thinking I'd be stuck with Thymeleaf when I started playing with Spring Boot.


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).

Thankfully, it's by no means a show-stopper for me thanks to their hot reloading functionality. https://templ.guide/commands-and-tools/hot-reload


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.


Why do you dislike code gen?


How does this compare to html/template in the Go standard library?

https://pkg.go.dev/html/template

Which is based on top of text/template.


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.


Both is possible. See here an example how to implement use case 2: https://github.com/ckilb/golang-layout-tpl-example

I don't like Go's template engine too much, I find it a bit cumbersome in many cases. But it's quite capable.


Here's a blog post which goes more into detail: https://kilb.tech/golang-templates


That is not quite a true "slot" behavior because the containing pages have to have a reference to the main layout via `{{ define "main" }}`


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.


Doesn’t go template have a way to define functions? You could then use a function to do w/e you like.


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.


If you pass a struct as input, you can check data & constraints. That's more or less what this is doing as well


I'm not sure what you mean. Eg, here: https://gist.github.com/thraxil/5ee7cdb4edb3b4846580e33f17ec...

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).


That's something that should be caught at test time. It's like having a bug in your code.

There is another class of template bugs that comes from input data, that's what I'm referring to. The validation catches it before rendering


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.


You have to introduce a new tool to your box and run more commands, so it is not free

people have a resistance to increasing tooling, and especially learning a new syntax or language, for a small task in a larger project

Since we test already, adding a test is often a much easier ask and lift

on top of that, you still need to validate inputs and test rendering with Templ anyway


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.


It's different from Jinja, but feature-wise it's about on-par.


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.


Hugo has some very hairy parts that make it very fast. I wouldn’t assume that it’s easily replaceable if you want the same performance?


Hugo's speed is largely from Go.

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.


This is pretty useful https://www.jetbrains.com/help/go/integration-with-go-templa... to improve productivity when working with templates in Go


There's an episode of the podcast Go Time hosting "Adrian Hesketh, the creator of Templ, and Joe Davidson, one of the maintainers on the project."

https://changelog.com/gotime/291


So I guess SSR is back on the menu? I wonder what the workflow is like.

If one wants to go Go+HTMX route, how does one write a webapp with handling auth and sessions and everything? I assume the backend is not just an API?

It'd be nice if one points to an example repo developed with this method.


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.


Yeah this seems like more of a majestic monolith approach.


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.


Isn't this basically PHP, but in go? Or am I missing something?


More like PHP and Pug, but in Go. As someone who likes server-rendered websites but prefers Go over PHP, I like this approach.


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 is html templating, but php's sore points were not the html templating.


I keep saying that PHP is above all a templating language. Last time I looked every file has to start with a php-mode tag still, for example.

Which makes it funny to then realize that e.g. twig is a templating language written in a templating language.


It’s funny but also sad.

There is a very simple and productive subset of PHP that I feel got neglected over the last 1-2 decades.

The simple, but relatively fast “array”, which has value semantics.

The batteries included networking (HTTP, SMTP etc.) and SQL stuff.

The stateless execution model and straightforward way to combine and execute scripts.

And finally the in-built, seamless HTML templating.

It’s unfortunate that especially the last one got neglected so developers feel the need to build a safe abstraction over it.


Yup. Unfortunate indeed.

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.


HTML templating in PHP is unfortunately unsafe.

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


Here is an example of an old technology

TAL -Template Attribute Language

https://en.m.wikipedia.org/wiki/Template_Attribute_Language

which happens to have an (also old) implementation i Go https://github.com/owlfish/tal

I wonder what opinion HN has on this type of templating language?


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.

This seems to be much nicer.


Codegen based?

Edit: Yep, codegen based.


Emacs support for syntax highlighting of .templ files would be helpful.

https://templ.guide/commands-and-tools/ide-support


Great project. Part of my go to stack now (FE:Templ+Htmx BE: Go,sqlc, postgres)


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.


This is great.

Is there a way to do code colocation with templates? I mean for writing presentation logic alongside the html.

Asking because it seems templ functions only allow html.


useful for generating tedious HTML emails e.g. export Thread to Email.


A great project!


Would have been amazing 10 years ago. Client side js is what would make this messy to use in big projects imho.


There's a huge swathe of projects that have no (or very little) use for client side js.


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.


Depends. If you know at the outset whether your client side is primarily an app or primarily a document, you can choose libraries that make sense.

If you're going to shoehorn everything into "this is an app" or "this is a document" then you're going to have problems no matter what you choose.


I think it still makes sense to make web apps that can function without JS.


I use templating in rust (jinja) even with rather some JavaScript.

My JavaScript modifies the DOM rather than creating it.


This can get hairy very fast.

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.


I disagree with that premise.

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.


Stealing this quote for future use:

> My JavaScript modifies the DOM rather than creating it.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: