This is great. I had an idea to use named iframes and targeted forms for simple, server-rendered pages with built-in style-scoped widgets, without leaning into complex JS client-side. But, I never simplified it well nor expressed a polished and elegant realization of that idea, as this htmz looks to me to be.<p>A reminder to never give up good ideas, focus on excellence, and focus on refinement to a completion of an idea, and communicate well!<p>Also the comments here:<p>- <i>This is a great hack and shows how close the browser is to offering SPA natively.</i><p>- <i>This is a glorious demonstration of someone really understanding the platform.</i><p>- <i>Simple and powerful, as the vanilla web should be. Thank you for this (small) gem :)</i><p>- <i>This is a neat hack, I like it :). Thanks for sharing.</i><p>are <i>exactly</i> what I hoped to hear reflected about my creation, and are totally on point for what this type of thing should be. Close to the web-grain, using the given material of the web in the best way possible. Fuck yeah! :)<p>Thank you for being a genius! :)<p>And for inspiring about what's possible! :)<p>P.S - also your communication and marketing skills are top notch! I think the way you have communicated this elegant technical thing, from the choice of name, API, examples, copy -- is just awesome. I learn from it! :)
That's a great hack and it shows how close the browser is to offering SPA natively.<p>Just a few attributes and we could avoid the iframe.<p>It's probably more useful to prove a point than an actual day to day tool. And the point seems to be: htmx is too much trouble for what it offers. We just need HTML native ajax.
I think there's a pretty strong argument at this point for this kind of replacing DOM with a response behavior being part of the platform.<p>I think the first step would be an element that lets you load external content into the page declaratively. There's a spec issue open for this: <a href="https://github.com/whatwg/html/issues/2791">https://github.com/whatwg/html/issues/2791</a><p>And my custom element implementation of the idea: <a href="https://www.npmjs.com/package/html-include-element" rel="nofollow">https://www.npmjs.com/package/html-include-element</a><p>Then HTML could support these elements being targets of links.
In 2001 or so, I was building an HTML based email client. We used a hidden iframe to execute JS to load data from the server and manipulate the DOM with the result. It was not quite as neat and elegant as this—the browser support was not there. However, the basic mechanism was the same.<p>It warms my heart to see the basic mechanism in such a compact package, without libraries upon libraries and machinations to make things work. This is probably perfect for 90% or so use cases where react or similar FE frameworks are used at the moment.<p>We later used to joke that we used Ajax before Ajax was a thing.
This is a glorious demonstration of someone <i>really</i> understanding the platform.<p>I don't expect I would ever use it, but I think it's excellent.
Further size reduction, you don't need the `this.` on the inline event listener, so it can be: `contentWindow.location.hash` and `contentDocument.body.childNodes` instead of `this.contentWindow.location.hash` or `this.contentDocument.body.childNodes`.<p>This will shave another 10 bytes off the snippet :D
Given that this uses `target`, doesn't it mean that unlike htmx you can't easily make this gracefully degrade when JS isn't enabled?<p>And, yes, I know, saying "when JS isn't enabled" in 2024 is a bit like saying "when the user is on Mars and has a 10 minute RTT" but forgive me for being an idealist.
Reminds me of pjax [1], except pjax works over XHR instead of an iframe and uses pushState by default to keep the back button working.<p>[1]: <a href="https://github.com/defunkt/jquery-pjax">https://github.com/defunkt/jquery-pjax</a>
Reusing the <slot> element like this is a bad idea - it has very specific behavior in browsers. In a shadow root it'll be replaced by the children of the host element, no matter what the library does.<p>HTML already has an inert <output> element for things like this.
I think the demo section needs work.<p>Clicking a "tab" to change the example code to Greeting, or anything else adds a history event but doesn't update the url.<p>I probably would have done the exact opposite in both aspects. Use replace to prevent extra navigation entries, but still update the url for bookmarking etc.<p>For something that claims to "just be html", it seems to be breaking some fundamental rules of the web/UX. Whether it's a simple mistake or not and easy to fix, it does not inspire confidence in the framework.
This seems snappy from the US but I doubt someone in say NZ will have a good experience. Going back and forth between the client and the server on every interaction can result in terrible UX.<p>Users will be happy waiting 1-2 seconds after submitting a form but waiting that much to switch a tab is not gonna fly. Plus there's internet weather etc which might result in unpredictable latencies over long distances.<p>Yes, you can move the compute layer of your app close to the user in multiple ways. Moving the data to the edge is much harder.
It's quite good. I noticed that my back button had to be repeatedly pressed to go back to write this comment, after interacting with the examples a few times. I'm sure that's simple enough to fix.
I happened to spend a little more time on htmx this weekend which htmz was inspired by.<p>htmx/htmz does do well for simple use cases, htmx does well for SSR heavy cases(e.g. django).<p>in the end I returned to vue.js, with a few lines code(put in a library) I can mimic htmx easily plus all the extra stuff vue.js brings, and no I do not need use a build tool for that, vuejs works fine via CDN inside a html for simple use cases the way htmx does, but vuejs can do more, e.g. draw live chart from data returned via ajax calls where vuejs is json by default, htmx is html by default instead.
This seems likely to have issues with most "Content-Security-Policy" rules because of the inline script in "onload" and the iframe. Makes it a non starter in real world production environments.
Interesting. I wrote a similar plain HTML/WebComponent-based front end for my new no-code/low-code serverless platform <a href="https://saasufy.com/" rel="nofollow">https://saasufy.com/</a><p>It lets you build just about any data-driven application using only a handful of declarative generic HTML components: <a href="https://github.com/Saasufy/saasufy-components?tab=readme-ov-file#saasufy-components">https://github.com/Saasufy/saasufy-components?tab=readme-ov-...</a><p>I built a chat app with both group chat and private chat (with access control) using only plain HTML; only ~250 lines of HTML markup for the entire thing, no front end or back end code was used: <a href="https://github.com/Saasufy/chat-app/blob/main/index.html">https://github.com/Saasufy/chat-app/blob/main/index.html</a><p>You can use it here - All hosted on GitHub pages: <a href="https://saasufy.github.io/chat-app/" rel="nofollow">https://saasufy.github.io/chat-app/</a><p>It would be great to add support for other front end tech like this. I kind of like HTMX (especially as it's declarative). This HTMZ looks interesting. I'd like something with a big community and more components to handle more advanced cases (e.g. higher level components like calendars).
Very cool snippet.<p>This is what I now wished existed. A flowchart/wizard that let you choose a development framework based on some questions and answers. So that a minimum framework (HTMZ) is used if it can satisfy. Or HTMX if one of your answers indicates that it's needed. Or Vue, etc. - getting "heavier" platforms as needed.<p>Of course we don't always know ahead of time the answers to the question. But being given the questions and the flowchart would be beneficial for the up front analysis.
Well, you are using javascript anyway so here it is a 163 bytes solution without the back button problem and iframe hack:<p><script>onclick=async e=>{x=e.target.dataset.x;if(x){e.preventDefault();document.querySelector(x).innerHTML=await fetch(e.target.href).then(r=>r.text())}}</script><p><a href="/somepage" data-x="#lib">Hi</a><p><div id="lib"></div><p>doesn't works with form though.
This looks neat! I've never really been in to web development, but I'm curious... is it possible to create a standalone .html file for a browser-delivered app? Like, not just PWA or SPA, but... a single HTML App?<p>If I had modest amount of data in JSON baked into html, what's the barrier to something interesting, say... implementing a minimal spreadsheet or maybe just a sortable/filterable table?
This is fantastic. Right now in my PWA I'm using a little library I created called `html-form` (HTMF) and I need to implement it as a SPA to avoid service worker fetching on every page change. So, I used a similar attribute to `hx-select`. But it adds quite a bit of code to the code base. Using your pattern I can remove all that added code and I can stop using hash navigation. Nice and simple.<p>Thank you for putting your code out in the public so I can learn from it!
Here a version that works for cross origin requests.<p><pre><code> <iframe hidden name=xhtmz onload="onmessage=x=>document.querySelector(x.data.target).innerHTML=x.data.content"></iframe>
</code></pre>
but website you are requesting has to embed the below script tag.<p><pre><code> <u id=t>just some other text</u>
<script>
parent.postMessage({
target:location.hash,
content:b.outerHTML
},'*')
</script>
</code></pre>
You would still set the DOM destination on the main page.<p><pre><code> <a href="https://xorigin.com#view" target=xhtmz>dog</a>
<div id=view></div></code></pre>
One downside of this approach is that it fills my browser history with a bunch of extra entries, which ain't ideal (especially for my Back button). I'd guess that's probably fixable as an htmz "extension"?<p>That aside, I love the concept.
<p><pre><code> <base target=htmz>
<iframe hidden name=htmz onload="..
</code></pre>
I'm somewhat frightened to ask if there's some special voodoo need for leaving off the quotes on htmz...
Very neat use of existing HTML. One thing this highlighted for me is that the MDN docs on the target attribute are incomplete, because I was recently reading them and while they mention that you can target iframes, they didn't actually describe <i>how</i> to do so.<p>I was reading the docs because I had some thoughts on my own htmx-like take. You've used some of the same attributes I was going to use but slightly differently than how I was going to approach it. Some good food for thought!
I wonder how feasible it is to create htmz / htmx -like lightweight library with the support for React/Vue/Stelve using web components. I agree that 90% of the use-cases you don’t need React but for the last 10%, most of us are stuck with these bloated frameworks. Astro has a similar idea but it’s working as a full framework instead of being a library. Considering the limitations with Astro, I guess the biggest bottleneck is state management.
Where did those basic (<100 loc) jQuery / XHR wrappers go that simply hijacked all links & forms and turned them into AJAX requests? That seemed like the sweet spot of having apps that worked well for search engines to index (SSR) and yet still offered better page transitions than regular full-page-load requests when you had JavaScript enabled.
Was curious to see the code so found the GitHub. Posting it here in case anyone is interested since the author doesn't link to it on the site and also the npm page doesnt link to it either<p><a href="https://github.com/Kalabasa/htmz">https://github.com/Kalabasa/htmz</a>
Except using a URL fragment refers to a “resource that is subordinate to another, primary resource” not a destination. They point out the URL abuse, so why do it?<p>Slot is also a part of the custom elements standard, but they say no custom elements.<p>Why use only web standards and then use them incorrectly?
I'll take a deeper look in the code later, but it seems useful.<p>If been using the window location hash to do some simple navigation on my SPA, but i use JS. (Just hide all sections and shows the one that matches the hash i.e.: #main
ha, I'm working on a dev-side version similar to this (mostly just for me, but hopefully publishable). I opted for an entirely pre-deplopyment build tool, where you just put<p><pre><code> <custom-tag custom-param="value"></custom-tag></code></pre>
in your HTML, run the build and it outputs the filled in file somewhere else. I know its functionality is very similar to many web frameworks (e.g. React, handlebars) but it does <i>one thing</i>.
> Not even a backend is required.<p>> In a nutshell, htmz lets you swap page fragments with HTML from the server using vanilla HTML code.<p>So a backend is needed....
It's a fun one liner, but what is the use case?<p>When I want to replace some element using JS as the user clicks a <i>link</i>, it is progressive enhancement.<p>Usually links enable history navigation. If you do stuff like this, you need to code it in a way that uses the history API to fix it for users with JS enabled (majority of users).<p>If you don't want history navigation and URLs, why do you use links?<p>This breaks history navigation and bfcache for no good reason, or am I missing something? bfcache already provides SPA-like speed, or even better.<p>No need to avoid regular links if you e.g. link from a list to a detail page.<p>Also:<p>> No preventDefaults. No hidden layers. Real DOM, real interactions. No VDOM, no click listeners. No AJAX, no fetch. No reinventing browsers.<p>So many words saying nothing, just to cater to some sentiment. fetch is part of browsers by the way.<p>If I need to replace an element as the user clicks a <i>link</i>, I can code it myself (without using this abstraction layer, however thin it is). I also don't need an iframe for doing this. And <i>preventDefault</i> is aptly named and a good reminder for what progressive enhancement should do. If it's not meant to be a link, don't use a link.<p>And if you want to react to clicks, you know, use click listeners (event handlers). Where's the problem?<p>It is understandable to developers and uses native browser functionality as intended. As opposed to this hack, which I'd find pretty glaring, bug-prone and hard to understand, would I have to debug an issue on some page that uses this snippet.<p>To me this seems like useless indirection and technical debt.
If you really need low-code AJAX "links" (who says you need that, if you don't want an SPA?), code yourself some understandable JS that is properly tailored to your site and respects history navigation, or use a library that is a good fit for your concrete use case.<p>As a joke, I like it though…