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.
There is no such technique. One way to distinguish is to pick a URL convention and modify the URL (before the hash) of the iframe URL. For example, add ?iframe=true to the URL, and then have the server check for that. Perhaps more usefully you could include information about the parent URL, e.g. url += '?parent=${document.referrer}'. Or something.
Edit: rather than a header, dynamically adding a query parameter to the URL of the element that was clicked would probably fit better with htmz's approach.
https://addons.mozilla.org/en-US/firefox/addon/disable-javas...
Could you describe your ideals for why websites should gracefully degrade without JS enabled? It’s not an unpopular view on HN, but from my perspective as a web developer, JS is a part of browser application just like HTML, and there’s no reason for the website to work if you’ve disabled a part of the browser.
I suspect “doesn’t have JavaScript” is being used as a proxy for a lot of other ideals that I do understand, like “should work on as many devices as possible” but that’s a rough correlation and doesn’t make the use of JS inherently bad.
Especially my ideal is that all functionality which can work without JavaScript should work without JavaScript. So, for example, I am not expecting someone to implement drag-and-drop calendars without JS, but there's no reason why the editing function of a calendar item should fundamentally require JS.
That being said, I know this is an idealist position, most companies which work on developing web-applications simply don't care about these niche use-cases where JS isn't an option and as such won't design their web-applications to accommodate those use-cases to save on development costs and time. But, while I am not really a web-developer, whenever I do deal with the web, I usually take a plain-HTML/CSS first approach and add JavaScript later.
Degrade gracefully is a required development skill. Sites need to allow for their pages to work in limited fashion without JS. JS should only be a layer added for interactivity, animation, and app construction. Otherwise, workarounds are great.
Is there a way to make this gracefully work? YES!! Instead of using hash tag names, use '?id=example'. And let the script in frame figure out the real destination of the output. Otherwise, the page will load the full site. Also use script to add "target" attribute to links.
If you just want to consume text, images, audios and videos, follow links and fill in forms (and that's quite a lot and pretty awesome already), you shouldn't need JavaScript.
So then, most JavaScript on the web is enabling revenue generation rather than improving the user experience. So yeah, disabling JS is a proxy for, “don’t waste my time.”
But I agree that it’s not inherently bad, but just mostly bad (for the user.)
The people who do disable JavaScript completely are admittedly few and far between, but are, I would assume, more common among the Hacker News crowd.
But in 2024 it's standard accepted practice. And that standard has made it so browser developers have to literally prevent the user themselves from having control over their browser because it's too dangerous to do otherwise.
The problem with the entire commercial web application ethos, despite it being a perfect fit for for-profit situations, is that it forces the rest of the web stack to gimp itself and centralize itself, CA TLS only, etc, just to keep the auto-code executing people secure. The one horribly insecure user behavior (auto executing random code) takes over from all other use cases and pushes them out.
So, we end up with very impressive browsers that are basically OSes but no longer functions as browsers. And that's a problem. Design your own sites so that they progressively enhance when JS is available. When work requires you to make bad JS sites, do so, but only in exchange for money.
A JS engine pre-supposes many, many things (too many) about a client, stuff like implicit assumptions that "this device has enough power and bandwidth to process my <insert length> javascript, perform appropriate security checks on it, handle this fast enough to service other requests in a timely manner, and also not take over the user's entire computer".
Accessibility means you should presume the least number of things possible. There's no sound, no visuals, no powerful cpu (maybe there isn't even a cpu), the device is the only working device from 20 years ago performing some critical function (e.g. government poverty assistance), there's only a couple minutes of power left, the internet is not available currently or quickly, etc.
You should never assume you have JS, period, and features should gracefully degrade in the presence of JS engines.
Reimplementing browser functionality in JS also often breaks excpectations as things don't work quite the same for all browsers. It also means browser extensiosn are less likely to be able to deal with the content.
Essentially it's like wanting your documents in PDF format even though a executables are also part of the PC platform and you could just ship a custom exectuable that renders your document instead. To me JS is just as absurd.
With only 181 bytes it could even be included in the page. It's much less than the sum of the meta tags on many sites.
I had access to their facility (dreams being as dreams often are), and managed to set it for 1992, and right when I was about to press the button, I woke up.
It RUINED my day.
Just a few attributes and we could avoid the iframe.
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.
From a practical perspective, a lot of the bulk of htmx is bound up in things like history support, collecting inputs, a lot of callbacks/events to allow people to plug into things, etc. I expect a lot of htmx-like libraries will come out now that it has some traction: it's not a super complicated idea, and many of them will pick smaller, more targeted subsets of functionality to implement. That's a good thing: the ideas of hypermedia are more important than my particular implementation.
I am now officially emotionally invested in htmx so I have to justify investing my time/energy in htmx instead of something else, so all alternatives to htmx are stupid and their existence make me very angry.
Thanks for these tools! I wanted to also take the opportunity to ask you something you either mentioned, commented or heard in a podcast.
You said that htmx might not be the tool to solve all problems (or certain kind of apps). Just asking because I think is also great to hear when not to use something to solve a problem. So, in your opinion what kind of webapps do you consider that maybe htmx is not a good fit? And in that case what alternate tools/approaches do you suggest? Once again thanks a lot for htmx & hyperscript!
I'm going to be doing a lot of web pages in HTMX in the next couple of years, and it will be much easier to develop/debug than javascript.
Have you thought about moving more features to extension, like was done with the web sockets feature?
I would fear if anyone wants to use this in production BUT I would love someone to get inspired and use the concepts rather than the actual code. Hmm maybe i should write a disclaimer...
I solved it by writing a discovery service... a wrapper that's accessible on a public (non-SSL) web page that basically opens a pile of hidden iframes and uses them to war dial local IP addresses until it finds the app and replaces the page's content with the winning frame's. Amazingly this janky thing has held up ;)
Anyway, nice work, and a cool idea!
In he spirit of breaking apart HTMX piece by piece, I created hyop (Hypermedia/Hydration Operation). It weighs in at 61 B but uses the "hyop" attribute. I know, I know, I'm bad...but at least I save bytes.
https://github.com/hyopjs/hyop
I'm going to use some of your concepts, with credit of course...like the snippetware idea among others.
Why? How would it be different from using htmx?
Perl on the back-end and some very tiny JS on the front-end. Do I need to tell U that this worked as absolute charm and was blazing fast. The only considerable downside was that indeed lots of traffic was going back and forth. But then 20 years later latency is much lower, traffic much cheaper, CPUs also, and I am very happy to see more and more people realize this bare-bones approach was actually a good thing to consider (the author lists the downsides).
To me such approach is much more web-native in comparison to abomination UI frameworks, that try to reinvent presentation marginalizing the browser to be nothing more than a drawing surface. But guess what - their primary goal is to save on this network latency, that is anyway going down down down down with every year.
The htmlz/htmlx approach is indeed much simpler and easy to live with in a large project, it is really sad that we put so much logic in the front-end in recent years...
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.
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.
In the tabs example, my question is always how dynamic and complex the tabs are. For fairly simple tabs its pretty clean to server render the HTML for all tabs and use a basic client-side script to handle navigation (ideally its synced in the URL as well).
For more complex tabs, or tabs heavily dependent on dynamic state, I find that its much easier to maintain long term if the rendering happens server-side. I personally have enjoyed HTMX for this to swap out an island of HTML, but a full page reload has worked for decades now.
In my experience the worst solution is shipping the complex state and the complex rendering logic to the browser. From performance to bug reproducibility, it has always cause me more pain that the DX original dev time are worth.
Sometimes it's interesting to see how far you can go without going the "let's download the web in a JS blob" way.
One of these days, I'm going to see whether I can still build something reasonably modern with Seaside, the continuation-based framework from the olden days, where basically the whole UI state is stored in the server-side session and everything is a request. Worked quite alright, way back when not everything had a local CDN and the internet tubes were a bit narrower.
If you are concerned about the delay when switching a tab, nothing prevents you to load both tabs during the first page load, and then just display the content without a new network requests. Whether you should do this or not is completely unrelated to whether you should use htmx, htmz or react.
The big killer here isn't just round trip time, it's the RTT compounded with the 14KB limit from TCP before it stops blindly sending data and compounded with extra resources that have to be fetched in a chain. If you keep the size of the response under 14KB and don't have render-blocking links to CSS and JS a page will be pretty fast; the moment it's any bigger the time before page load will double. I'm happy waiting about 300ms for a page served from the east coast of the US to load, but 600ms becomes noticeable and anything more - JS which loads more JS which loads JSON and blocks rendering until it's done, for example - starts to be excruciating.
Avoiding delays for what should be purely client-side actions is a non-negotiable to me.
That being said, usually people writing downloads using fetch will implement a promise-based timeout on top of the network timeout, which causes all kinds of issues on 2g internet. One of my favorite side-effects are implementations that "retry after 2s" that don't actually cancel the in-flight request, causing my entire bandwidth to be hogged up with dozens of these requests "retrying" when the initial request eventually returned successfully. Javascript developers are unintentionally evil to non-5g internet.
Which means its gonna be the same thing if you try to render on the client, since you still need a round trip for data...
A reminder to never give up good ideas, focus on excellence, and focus on refinement to a completion of an idea, and communicate well!
Also the comments here:
- This is a great hack and shows how close the browser is to offering SPA natively.
- This is a glorious demonstration of someone really understanding the platform.
- Simple and powerful, as the vanilla web should be. Thank you for this (small) gem :)
- This is a neat hack, I like it :). Thanks for sharing.
are exactly 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! :)
Thank you for being a genius! :)
And for inspiring about what's possible! :)
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! :)
Who are you and what is your relationship with htmz and its creators? Please be honest and refrain from violating federal law and FTC guidelines in your response.
"I had an idea to use named iframes [...] But, I never simplified it well nor expressed a polished and elegant realization of that idea, as this htmz looks to me to be. [...] the comments here [...] are exactly what I hoped to hear reflected about my creation, and are totally on point for what this type of thing should be."
Seems pretty clear to me. This person had a similar idea but didn't complete it, and finds the flavor of appreciative "nice hack" energy (as opposed to "this is enterprise" or "this is a revolution" energy, I guess) appropriate for the project and the type of feedback they had wanted to hear had they completed their project.
My name is John Galt and I wrote this library. When you learn who I really am, then you'll understand.
When I want to replace some element using JS as the user clicks a link, it is progressive enhancement.
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).
If you don't want history navigation and URLs, why do you use links?
This breaks history navigation and bfcache for no good reason, or am I missing something? bfcache already provides SPA-like speed, or even better.
No need to avoid regular links if you e.g. link from a list to a detail page.
Also:
> No preventDefaults. No hidden layers. Real DOM, real interactions. No VDOM, no click listeners. No AJAX, no fetch. No reinventing browsers.
So many words saying nothing, just to cater to some sentiment. fetch is part of browsers by the way.
If I need to replace an element as the user clicks a link, 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 preventDefault 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.
And if you want to react to clicks, you know, use click listeners (event handlers). Where's the problem?
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.
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.
As a joke, I like it though…
The marketing pitch on the landing page is written so well that I took it too seriously probably.
I know htmx but wasn't able to see this as a parody, so my fun capabilities were failing :)
I don't expect I would ever use it, but I think it's excellent.
It's a fun project overall, though.
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.
We later used to joke that we used Ajax before Ajax was a thing.
This is the problem with technology. When it works well and really solves the problem, it is invisible. No one gets promoted for invisible.
This will shave another 10 bytes off the snippet :D
setTimeout(()=>document.querySelector(contentWindow.location.hash)?.replaceWith(...contentDocument.body.childNodes))
Although location.hash defaults to the empty string '' when there is no hash, which gives a SyntaxError, so we still need the fallback selector to select none.
HTML already has an inert <output> element for things like this.
Edit: that is, after I wake up...
Anyway I chose div instead thanks to the other commenter https://github.com/Kalabasa/htmz/commit/153a5a448b60a0898604...
htmx/htmz does do well for simple use cases, htmx does well for SSR heavy cases(e.g. django).
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.
I think Vue.js "scales down" excellently. You can just load it via the cdn and write some widgets and go about your day.
In the past I've tried to make use of "microframeworks" like alpine.js and such but often found myself returning to Vue.js.
vue.js does not mix SSR with SPA into one, make it much simpler compare to what React.js is doing today, and it provides way more than alpine.js and htmx etc, it's the best one in practice for me now.
<base target=htmz>
<iframe hidden name=htmz onload="..
I'm somewhat frightened to ask if there's some special voodoo need for leaving off the quotes on htmz...I've found this article a handy reference on all the stuff it's safe to leave out
> * Is htmz a library or a framework?
> htmz is a snippet.
> htmz is a minimalist HTML microframework
So, some ambiguity.
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: https://github.com/whatwg/html/issues/2791
And my custom element implementation of the idea: https://www.npmjs.com/package/html-include-element
Then HTML could support these elements being targets of links.
I think some of the proposals I see in that thread are kind of interesting, but to do it properly requires a new MIME type for html fragments, eg. text/html-fragment. This is arguably what htmx should also be using for fragments.
I do kind of like the idea of adding this as an attribute to some existing elements, and they show the inner content by default until the target is loaded. Basically like adding an iframe-like rendering mode to existing elements. Then you could implement htmx's hx-boost behaviour just by setting this mode on the body element and using the htmz trick of links naming the body as a target.
Clicking a "tab" to change the example code to Greeting, or anything else adds a history event but doesn't update the url.
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.
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.
<a> elements add to the history, it's what they're supposed to do. If you don't want them to do that, don't use <a> elements for UI. Use a button and change the iframe src with javascript. That's not really the library's problem, and it's not really hypertext's problem what the browser is putting into history.
> For something that claims to "just be html", it seems to be breaking some fundamental rules of the web/UX.
Yes, obviously. HTML+CSS are meant to put boxes and words and images on a screen. If you want to show a spreadsheet with cell formulas, use javascript. Fundamental rules of web/UX are not the same as fundamental rules of hypermedia.
> Whether it's a simple mistake or not and easy to fix, it does not inspire confidence in the framework.
There's a section on the page titled "Is this a joke?" (not to mention, "Is htmz a library or a framework?"- its a snippet). Confidence is not the point. The point is to demonstrate how little it takes to turn HTML into a stateful UI. As the author says:
> If you need something more interactive than the request-response model, you may try the htmz companion scripting language: javazcript. Sorry, I meant JavaScript, a scripting language designed to make HTML interactive.
> htmz does not preclude you writing JS or using UI libraries to enhance interaction. You could, say, enhance a single form control with vanillaJS, but the form values could still be submitted as a regular HTTP form with htmz.
It lets you build just about any data-driven application using only a handful of declarative generic HTML components: https://github.com/Saasufy/saasufy-components?tab=readme-ov-...
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: https://github.com/Saasufy/chat-app/blob/main/index.html
You can use it here - All hosted on GitHub pages: https://saasufy.github.io/chat-app/
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).
I think I know what you mean, but we clearly have different definitions of "plain HTML." Mine wouldn't involve 9 javascript files. :-D
Either way, I'm interested and will check out what you made!
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?
Yes, you can include your JavaScript and CSS directly inside the <script> [0] and <style> [1] tags, so you don't need to include any other file. Images like PNG, JPEG, ... can be either embedded with a base64 data URL [2] or an SVG with the SVG tag [3].
> what's the barrier to something interesting, say... implementing a minimal spreadsheet or maybe just a sortable/filterable table?
Well, you could go the easy route and use an already existing JavaScript library for this. Libraries are normally included via a URL, but you can just copy the contents of this library into the script tag as mentioned above.
Otherwise, I think it's manageable to do develop it yourself (sortable/filterable tables) much knowledge, but frontend development can be a PITA very fast
[0] https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sc...
[1] https://developer.mozilla.org/en-US/docs/Web/HTML/Element/st...
[2] https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_...
[3] https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
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
Oh there is a good hack you can do here! I've been meaning to write a blog post about this exact thing!
See CSS `:target` selector
section {
display: none;
}
section:target {
display: block;
}> In a nutshell, htmz lets you swap page fragments with HTML from the server using vanilla HTML code.
So a backend is needed....
Edit: on second thought, direct filesystem access has different origins which would mess with iframes. I'm not on the computer now to test. But at the very least you need a basic web server that serves files.
But iframes can't do
* Deletion (e.g complete a todo list item)
* Tail end append (e.g lazy loading infinite scroll list)
So i pivoted to js...
<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>
<a href="/somepage" data-x="#lib">Hi</a>
<div id="lib"></div>
doesn't works with form though.
Thank you for putting your code out in the public so I can learn from it!
So, it seems like this pattern could probably take you quite a ways. But the main issue I have is that you would need to parse all that JS out twice by the browser. But if you don't have the need for any `script` tags in what you pass back it would be pretty nice.
Also, it wouldn't work for graceful degradation as the server wouldn't know if you need the whole page or a segment. As HTMX adds a header that lets you know that JavaScript is being used by the front end.
Overall, I think it is really cool and it would be neat to see how far you could take this.
<iframe hidden name=xhtmz onload="onmessage=x=>document.querySelector(x.data.target).innerHTML=x.data.content"></iframe>
but website you are requesting has to embed the below script tag. <u id=t>just some other text</u>
<script>
parent.postMessage({
target:location.hash,
content:b.outerHTML
},'*')
</script>
You would still set the DOM destination on the main page. <a href="https://xorigin.com#view" target=xhtmz>dog</a>
<div id=view></div>Slot is also a part of the custom elements standard, but they say no custom elements.
Why use only web standards and then use them incorrectly?
Because it's a clever hack, not a standards proposal. Lighten up, Francis.
> A string containing one or more selectors to match. This string must be a valid CSS selector string; if it isn't, a SyntaxError exception is thrown.
I'll test it out. If this works good, we shave off 5 bytes towards a 176-byte snippet!
- htmz on handcar (still on rails but you have more manual control)
- htmz on horseback (if you want to reject modernity)
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.
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.
That aside, I love the concept.
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!
<custom-tag custom-param="value"></custom-tag>
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 one thing.So little code, achieving so much!!!
No babel plugin? Npm module… sheesh /s