vlucas
15 hours ago
Good writeup. I am in the camp that Tagged Template Literals are the ultimate answer for easy templating that is native to JavaScript. The bit about using native Signals with them is spot on too. Nothing else really allows the clean separation of HTML strings with other random JavaScript objects and pieces as easily as Tagged Templates do. On the remarks about fear of strings, server-rendered markup is just one giant string sent to the browser anyways. I don't know why some people get so worked up about why they think templates should not be strings. That is literally what HTML shipped to the browser is. It's also the easiest and most straightforward way to define templates in native JavaScript without any compile step.
I did many of my own experiments with templates that are native JS with no compile step, and reached basically the same conclusion as this post. Use tagged templates and keep it simple. My angle was server-focused, with async bits in a template doing automatic streaming while server rendering with them. Works super well and it takes so little code to achieve. Check it out if you're interested: https://www.hyperspan.dev/ or the CodeSandbox demo: https://7lpdpl.csb.app/
jfagnani
15 hours ago
Interesting server framework! It looks very similar in some ways to a server I started last year out of frustration with Koa and Express called Zipadee: https://github.com/justinfagnani/zipadee?tab=readme-ov-file#...
The templates look basically identical. html-tagged template literals that support streaming and async values and automatically escape values.
What Koa does will allowing strings by be returned with a default HTML mimetype is security malpractice, IMO. It's way to easy to just interpolate user-controlled values.
vlucas
12 hours ago
Wow that is uncanny. We arrived at almost exactly the same solution and started working on it at almost exactly the same time. I went a little bit further with client islands, file-based routing, custom placeholders for async loading content in templates, etc. since I was looking to replace my own use of Next.js. But man. So close to the same thing. Small world. Cheers!
user
12 hours ago
jfagnani
12 hours ago
Very uncanny! I like it :)
I even have a lot of those things planned, just not enough time!
I didn't do anything that required client-side JS yet, but the first things on that list are out-of-order rendering; watch mode (page reload); and hot module replacement.
vlucas
9 hours ago
I checked your approach. My first attempt looked a lot like yours, but I used an AsyncGenerator. Then I benchmarked it and found out it was slow as heck so I started thinking about other ways to do it.
Iterables/Generators seem like they work well for this problem and it's the first thing I reached for too, but out-of-order streaming is really hard to do with the generator yield syntax. You have to yield things one by one as they come in, and you can't await or you're back to a slow AsyncGenerator. I wound up radically simplifying my template function to just return { content: string, asyncContent: Promise[] } instead and then made different render strategies that handle the resolution of the asyncContent differently: https://github.com/vlucas/hyperspan/blob/main/packages/html/...
I'd love to chat about the approach I used. My email is on my website.