mirror of
https://github.com/bigskysoftware/htmx.git
synced 2026-01-25 05:06:13 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
580549355a | ||
|
|
709512c1ac | ||
|
|
5a374d546b | ||
|
|
1a30b9130e | ||
|
|
ad65bc77ce | ||
|
|
381449089d | ||
|
|
2e229462e3 | ||
|
|
6b214f11e7 | ||
|
|
58dc1e247d | ||
|
|
749d5f2f4c | ||
|
|
fcfca903af | ||
|
|
563fff67db | ||
|
|
9c1297c5f3 | ||
|
|
e495b68dc3 | ||
|
|
3abaf7eb3f | ||
|
|
e9f2ee94e3 | ||
|
|
bced397c28 | ||
|
|
7a0086fceb | ||
|
|
2a1339287e | ||
|
|
a275707f4a | ||
|
|
f1e0b926d8 | ||
|
|
e71f746bad | ||
|
|
8b249b1544 | ||
|
|
b7f833b6d5 |
@@ -306,6 +306,17 @@ Thank you to all our generous <a href="https://github.com/sponsors/bigskysoftwar
|
||||
<img class="dark-visible" src="/img/exchange-rate-api-dark.png" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="mersano" href="https://instant-famous.com/">
|
||||
<img src="/img/rsz_instant_famous.png" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</div>
|
||||
<div></div>
|
||||
<div>
|
||||
<a data-github-account="blacksandsmedia" href="https://hellostake.com/au/referral-code">
|
||||
<img src="/img/stake.jpeg" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center;font-style: italic;margin-top: 26px;">ʕ •ᴥ•ʔ made in montana</div>
|
||||
|
||||
@@ -91,7 +91,7 @@ within the language:
|
||||
* Now any element, not just the entire window, can be the target for update by the request
|
||||
|
||||
Note that when you are using htmx, on the server side you typically respond with *HTML*, not *JSON*. This keeps you firmly
|
||||
within the [original web programming model](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm),
|
||||
within the [original web programming model](https://roy.gbiv.com/pubs/dissertation/rest_arch_style.htm),
|
||||
using [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS)
|
||||
without even needing to really understand that concept.
|
||||
|
||||
@@ -101,7 +101,7 @@ It's worth mentioning that, if you prefer, you can use the [`data-`](https://htm
|
||||
<a data-hx-post="/click">Click Me!</a>
|
||||
```
|
||||
|
||||
If you understand the concepts around htmx and want to see the quirks of the library, please see our
|
||||
If you understand the concepts around htmx and want to see the quirks of the library, please see our
|
||||
[QUIRKS](@/QUIRKS.md) page.
|
||||
|
||||
## 1.x to 2.x Migration Guide
|
||||
@@ -1163,7 +1163,7 @@ If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
|
||||
- Import both packages to your `index.js`
|
||||
```JS
|
||||
import `htmx.org`;
|
||||
import `htmx-ext-extension-name`; // replace `extension-name` with the name of the extension
|
||||
import `htmx-ext-extension-name`; // replace `extension-name` with the name of the extension
|
||||
```
|
||||
|
||||
Note: [Idiomorph](/extensions/idiomorph) does not follow the naming convention of htmx extensions. Use `idiomorph` instead of `htmx-ext-idiomorph`. For example, `https://cdn.jsdelivr.net/npm/idiomorph` or `npm install idiomorph`.
|
||||
@@ -1379,7 +1379,7 @@ Here is an example of the code in action:
|
||||
|
||||
## Scripting {#scripting}
|
||||
|
||||
While htmx encourages a hypermedia approach to building web applications, it offers many options for client scripting. Scripting is included in the REST-ful description of web architecture, see: [Code-On-Demand](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_7). As much as is feasible, we recommend a [hypermedia-friendly](/essays/hypermedia-friendly-scripting) approach to scripting in your web application:
|
||||
While htmx encourages a hypermedia approach to building web applications, it offers many options for client scripting. Scripting is included in the REST-ful description of web architecture, see: [Code-On-Demand](https://roy.gbiv.com/pubs/dissertation/rest_arch_style.htm#sec_5_1_7). As much as is feasible, we recommend a [hypermedia-friendly](/essays/hypermedia-friendly-scripting) approach to scripting in your web application:
|
||||
|
||||
* [Respect HATEOAS](/essays/hypermedia-friendly-scripting#prime_directive)
|
||||
* [Use events to communicate between components](/essays/hypermedia-friendly-scripting#events)
|
||||
@@ -1697,7 +1697,7 @@ for exploring this topic.
|
||||
|
||||
### CSRF Prevention
|
||||
|
||||
The assignment and checking of CSRF tokens are typically backend responsibilities, but `htmx` can support returning the CSRF token automatically with every request using the `hx-headers` attribute. The attribute needs to be added to the element issuing the request or one of its ancestor elements. This makes the `html` and `body` elements effective global vehicles for adding the CSRF token to the `HTTP` request header, as illustrated below.
|
||||
The assignment and checking of CSRF tokens are typically backend responsibilities, but `htmx` can support returning the CSRF token automatically with every request using the `hx-headers` attribute. The attribute needs to be added to the element issuing the request or one of its ancestor elements. This makes the `html` and `body` elements effective global vehicles for adding the CSRF token to the `HTTP` request header, as illustrated below.
|
||||
|
||||
Note: `hx-boost` does not update the `<html>` or `<body>` tags; if using this feature with `hx-boost`, make sure to include the CSRF token on an element that _will_ get replaced. Many web frameworks support automatically inserting the CSRF token as a hidden input in HTML forms. This is encouraged whenever possible.
|
||||
|
||||
@@ -1713,7 +1713,7 @@ Note: `hx-boost` does not update the `<html>` or `<body>` tags; if using this fe
|
||||
</body>
|
||||
```
|
||||
|
||||
The above elements are usually unique in an HTML document and should be easy to locate within templates.
|
||||
The above elements are usually unique in an HTML document and should be easy to locate within templates.
|
||||
|
||||
|
||||
## Configuring htmx {#config}
|
||||
|
||||
@@ -24,6 +24,7 @@ page_template = "essay.html"
|
||||
* [Does Hypermedia Scale?](@/essays/does-hypermedia-scale.md)
|
||||
|
||||
### Real World htmx Experiences
|
||||
* [Building Critical Infrastructure with htmx: Network Automation for the Paris 2024 Olympics](@/essays/paris-2024-olympics-htmx-network-automation.md)
|
||||
* [A Real World React to htmx Port](@/essays/a-real-world-react-to-htmx-port.md)
|
||||
* [Another Real World React to htmx Port](@/essays/another-real-world-react-to-htmx-port.md)
|
||||
* [A Real World wasm to htmx Port](@/essays/a-real-world-wasm-to-htmx-port.md)
|
||||
|
||||
@@ -66,6 +66,12 @@ a single, tidy package that is smaller than htmx.
|
||||
|
||||
You can see many examples of Datastar in action [here](https://data-star.dev/examples).
|
||||
|
||||
## Nomini
|
||||
|
||||
[Nomini](https://github.com/nonnorm/nomini) is a hypermedia implementation that embraces writing JavaScript in the original and intended way, as a simple enhancement to mostly-static pages. Its goal is to add a minimal layer of LoB on top of HTML to allow for powerful server-driven web apps with easily implemented client-side features. Additionally, it is currently the smallest library existing that gives both reactive variables and partial page swaps (~2.8k minified, ~1.4k minzipped).
|
||||
|
||||
In essence, Nomini is a tiny reimplementation of Datastar or a combination of Fixi and Alpine.js, intended to be a minimal, pragmatic building block for reactive server-driven UIs.
|
||||
|
||||
## Alpine-ajax
|
||||
|
||||
Speaking of Alpine (which is a common library to use in conjunction with htmx) you should look at
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
+++
|
||||
title = "Building Critical Infrastructure with htmx: Network Automation for the Paris 2024 Olympics"
|
||||
description = """\
|
||||
Building critical software infrastructure with htmx, and how the simplification induced by this approach \
|
||||
is interesting for AI-assisted development."""
|
||||
date = 2026-01-16
|
||||
authors = ["Rodolphe Trujillo"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
## A Bit of Background
|
||||
|
||||
During my 6 years at Cisco, I developed numerous web applications to assist network engineers with highly complex
|
||||
operations, both in terms of the volume of tasks to accomplish and the rigor of procedures to follow. Networking is a
|
||||
specialized field in its own right, where the slightest error can have disastrous consequences: a network failure, even
|
||||
partial, can deprive millions of people of essential services like the ability to make a simple phone call.
|
||||
|
||||
This criticality imposes strict requirements on code meant for network operations: it must be reliable, readable, and
|
||||
free of unnecessary frills. If there's a problem, you need to be able to immediately trace the data flow and fix it on
|
||||
the spot. That's why, for years, I've used very few design patterns and banned function calls that call functions that
|
||||
call functions, and so on. Beyond 2 levels of calls, I abstain.
|
||||
|
||||
Following this logic, I favor mature tools over the latest trends. Thus, the Django / Celery / SQLite stack had been in
|
||||
my toolbox for a long time. But like everyone else in the 2010s, I built SPAs and had never heard of intercooler.js or
|
||||
hypermedia, and I understood REST the way it's commonly described pretty much everywhere.
|
||||
|
||||
For the JS framework, I made a conservative choice (and a marginal one, I know): I chose Ember.js. My motivations were
|
||||
its strong backward compatibility during updates and native MVC support. This JS framework is excellent, and that's
|
||||
still my opinion.
|
||||
|
||||
After watching David Guillot's presentation on HTMX at DjangoCon Europe 2022, I dug into the subject and prototyped a
|
||||
component that addressed one of my recurring needs. It's a kind of datatable on which you can trigger actions. There's a
|
||||
demo video on the HTMX Discord [here](https://discord.com/channels/725789699527933952/909436816388669530/1042451443656966234).
|
||||
|
||||
I was a beginner with HTMX and built it in 2-3 days (no AI :-) ). But what was interesting wasn't so much having this
|
||||
component quickly at hand : it was the 100% Django codebase. One codebase instead of two, one app to maintain, and no
|
||||
more API contracts to manage between front and back.
|
||||
|
||||
And once again, even though I was comfortable with the Ember.js framework, having a single project to maintain changes
|
||||
everything.
|
||||
|
||||
A few weeks later, a concrete use case came up for a major French ISP: configuring L2VPNs on brand-new routers, in bulk,
|
||||
without configuration errors (obviously), based on configurations from old routers that were end-of-life and about to be
|
||||
decommissioned. It was highly critical: a single router can handle thousands of clients, and... there are a lot of
|
||||
routers.
|
||||
|
||||
From that point on, I used the Django / Celery + HTMX + SQLite (and Hyperscript) stack and delivered the app in 5 weeks.
|
||||
My goal was to guide the network engineer by the hand and spare them 100% of the repetitive, tedious work: they just had
|
||||
to click and confirm, everything was handled. Their role was now limited to their expertise, and if there was a problem,
|
||||
it was up to them to fix the network.
|
||||
|
||||
The project, initially estimated at 18 months, ultimately took 9. And we were lucky: there were no complex corner cases
|
||||
to handle. And even if there had been, we had plenty of time to deal with them.
|
||||
|
||||
HTMX in all this?
|
||||
If I had to develop the app as a SPA, it would have taken me at least twice as long. Why?
|
||||
As a solo full-stack developer, simply switching back and forth between codebases is already time-consuming. And that's
|
||||
just the tip of the iceberg: the front/back approach itself adds a layer of complexity that ends up weighing heavily on
|
||||
productivity.
|
||||
|
||||
## HTMX at the Olympic Games
|
||||
|
||||
The Paris 2024 Olympic Games network consisted of thousands of Cisco switches pre-configured to accept Wi-Fi access
|
||||
points, which self-configured through an automation system developed by Orange and Cisco. Wi-Fi was the most common
|
||||
connectivity mode at the Games. But in some cases, a physical connection was necessary, most often to plug in a video
|
||||
camera, but not only. Sometimes there was simply no other choice but to rely on a cable to connect, and therefore to
|
||||
configure the relevant switch port. That's where an application became necessary.
|
||||
|
||||
When Pollux contacted me about his need, he already had a data model for his network services in a Django project.
|
||||
Additionally, he could deploy services via CLI: part of the business logic was already in place. The problem was that
|
||||
service deployment parameters needed to be consolidated in an application. In CLI, you have to manage different data
|
||||
sources, which can quickly become complicated for the user. So it was necessary to centralize these business parameters
|
||||
in a webapp, expose all the data needed to deploy a service, and provide a GUI to configure them.
|
||||
|
||||
The Games were approaching and Pollux didn't have time to build the webapp: as the architect of the Olympic network, he
|
||||
was overwhelmed by a colossal number of tasks. I showed him the L2VPN app mentioned above and specified the 5-week
|
||||
delivery timeline. I told him that if it suited him, I could build him an HTMX webapp based on his existing Django
|
||||
project and a Bootstrap CSS customized internally by Orange.
|
||||
|
||||
We agreed on an 8-week timeline to cover the need, which involved 3 connectivity services: Direct Internet Access,
|
||||
Private VLAN, and Shared Internet Access.
|
||||
|
||||
## Web Dev with HTMX
|
||||
|
||||
HTMX is somewhat a return to the roots of web development, and regardless of the web framework: Django, ROR, Symphony...
|
||||
You rediscover everything that makes a web framework useful rather than turning it into a mere JSON provider. Sending
|
||||
HTML directly to the browser, storing the app state directly in the HTML. That's what true REST is, and it's so much
|
||||
simpler to manage.
|
||||
|
||||
If you ask me what's most striking, it's certainly returning to very simple things like this:
|
||||
|
||||
<figure>
|
||||
<center>
|
||||
|
||||

|
||||
|
||||
</center>
|
||||
<figcaption> Progress bar from RCP Portal </figcaption>
|
||||
</figure>
|
||||
|
||||
How does this progress bar work?
|
||||
|
||||
Exactly like [the example in the docs](https://htmx.org/examples/progress-bar/)!
|
||||
|
||||
Why this choice? Because it's coded in 10 seconds, because the app won't have thousands of users on this internal tool,
|
||||
no scaling concerns: you can do good old data polling without any problem.
|
||||
|
||||
And the end user? If I use old-school polling, they don't care: what they want is the information. No SSE or WebSocket
|
||||
for this use case, I don't need it. And if the need ever arises, the WebSocket (or SSE) plugin is easy to set up.
|
||||
|
||||
One of the big advantages of the philosophy surrounding HTMX is the notion of [Locality of Behaviour](@/essays/locality-of-behaviour.md). Let's take this
|
||||
progress bar: if you want to know how it works, just look at the page source. No need to go into documentation or the
|
||||
codebase, just a right-click and "View Page Source":
|
||||
|
||||
```html
|
||||
|
||||
<div
|
||||
hx-get="/job/progress"
|
||||
hx-trigger="every 600ms"
|
||||
hx-target="this"
|
||||
hx-swap="innerHTML">
|
||||
<div class="progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"
|
||||
aria-labelledby="pblabel">
|
||||
<div id="pb" class="progress-bar" style="width:0%">
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
You immediately know that every 600ms, this part of the page is updated with the content returned by the view handling
|
||||
the `/job/progress` endpoint. No mystery for the team taking over development who wants to modify something: everything
|
||||
you need to know is right in front of you.
|
||||
|
||||
And that's exactly what HTMX is about: every component, every interaction remains visible, understandable, and
|
||||
self-documented directly in the HTML. This is important for what comes next.
|
||||
|
||||
## HTMX is "AI friendly"™
|
||||
|
||||
In the early stages of app development, I focused on the most complex network service: DIA (Direct Internet Access). DIA
|
||||
for the Olympic Games meant many business parameters with many rules to apply.
|
||||
|
||||
The DIA creation form calls an endpoint that triggers a very long function, close to 600 lines of code.
|
||||
|
||||
Why such a long function?
|
||||
|
||||
Because it's more readable and efficient to concentrate the data flow in one place, rather than dispersing it across a
|
||||
multitude of layers and patterns.
|
||||
|
||||
An application is a wrapper around data: it orchestrates the data flow (data must flow!) and CRUD operations. But what
|
||||
control do you retain when this flow is obfuscated in complex patterns or dispersed across 2 codebases?
|
||||
|
||||
The data flow must remain readable for the developer.
|
||||
|
||||
HTMX, by allowing you to manage the GUI directly server-side, makes this flow even clearer. The same endpoint can return
|
||||
HTML fragments to signal that certain form data is invalid, or conversely indicate that a service deployment has
|
||||
started. You can thus act on any part of the GUI within the same function, while transforming the data to pass it to the
|
||||
system that configures the network switch.
|
||||
|
||||
In a traditional frontend/backend approach, this would be more complex: two applications to manage, and a much less
|
||||
readable data flow.
|
||||
|
||||
This drastic code simplification enabled by HTMX, combined with a procedural approach, produces compact and transparent
|
||||
logic, easy to navigate for a developer... or for an LLM, as I discovered.
|
||||
|
||||
For the Private VLAN (PVLAN) network service, the "shape" of the main function is roughly the same as for DIA: input
|
||||
parameters, validation, then interactions with the GUI via HTML fragments, and, if everything is correct, switch
|
||||
configuration.
|
||||
|
||||
The difference? PVLAN is simpler to handle: fewer form parameters and a bit less business logic.
|
||||
|
||||
So I took the long DIA function and gave it to an LLM (Claude 3 had just been released), with a prompt specifying the
|
||||
parameters specific to PVLAN. In seconds, Claude returned a new adapted function, and the same for the HTML templates.
|
||||
Result: about 80% of the code was ready, with only a few points to correct and relatively few errors made by the LLM,
|
||||
which freed up time for me to add specific business logic for a major client.
|
||||
|
||||
For the third network service, Shared Internet Access (SIA), even simpler than the previous two, I provided the LLM with
|
||||
both the DIA and PVLAN functions. With the magic word *"extrapolation"* in the prompt, the generated code was 95%
|
||||
correct.
|
||||
|
||||
## Summary of My Experience
|
||||
|
||||
- **DIA**: 0% AI, 100% handwritten code (business logic + GUI + overhaul of the switch configuration task management
|
||||
system) → **4 weeks**
|
||||
- **PVLAN**: 80% AI, 20% handwritten code (corrections + adding specific business logic) → **1 week**
|
||||
- **SIA**: 95% AI, 5% handwritten code (minor corrections) → **1 day**
|
||||
|
||||
The time saved was reinvested in testing, bug fixes, project management, and even a few additions outside the initial
|
||||
scope.
|
||||
|
||||
Moreover, the same app was used on the "Tour de France 2025" with minor changes that were made easily thanks to the
|
||||
hypermedia approach.
|
||||
|
||||
This result is possible because of the combination of *HTMX + the procedural approach*, which produces naturally
|
||||
readable code, without unnecessary abstraction layers. The data flow is clear, concentrated in a single function, and
|
||||
the GUI/server logic is directly accessible.
|
||||
|
||||
For an LLM, this is ideal: it doesn't need to construct context through a complex architecture. It just needs to follow
|
||||
the flow and extrapolate it to a new use case. In other words, what's simpler for the developer is also simpler for the
|
||||
AI. This is the sense in which HTMX is truly *"AI friendly"™*.
|
||||
|
||||
Ultimately, HTMX mainly allowed me to save time and keep my code clear.
|
||||
No unnecessary layers, no superfluous complexity: just concrete stuff that works, fast.
|
||||
|
||||
And that has made a big difference on these critical projects.
|
||||
@@ -166,6 +166,7 @@ Here are some known implementations of the fragment concept:
|
||||
* [Giraffe.ViewEngine.Htmx](https://github.com/bit-badger/Giraffe.Htmx/tree/main/src/ViewEngine.Htmx)
|
||||
* Rust
|
||||
* [MiniJinja](https://docs.rs/minijinja/latest/minijinja/struct.State.html#method.render_block)
|
||||
* [Askama](https://askama.readthedocs.io/en/stable/template_syntax.html#block-fragments)
|
||||
* Raku
|
||||
* [Cro Templates](https://github.com/croservices/cro-website/blob/main/docs/reference/cro-webapp-template-syntax.md#fragments)
|
||||
|
||||
|
||||
269
www/content/essays/the-fetchening.md
Normal file
269
www/content/essays/the-fetchening.md
Normal file
@@ -0,0 +1,269 @@
|
||||
+++
|
||||
title = "The fetch()ening"
|
||||
description = """\
|
||||
You know, technically, I never said anything about a version *four*"""
|
||||
date = 2025-11-01
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||

|
||||
|
||||
OK, I said there would never be a version three of htmx.
|
||||
|
||||
But, _technically_, I never said anything about a version *four*...
|
||||
|
||||
## htmx 4: The fetch()ening
|
||||
|
||||
In [The Future of htmx](@/essays/hypermedia-driven-applications.md) I said the following:
|
||||
|
||||
> We are going to work to ensure that htmx is extremely stable in both API & implementation. This means accepting and
|
||||
documenting the [quirks](https://htmx.org/quirks/) of the current implementation.
|
||||
|
||||
Earlier this year, on a whim, I created [fixi.js](https://github.com/bigskysoftware/fixi), a hyperminimalist implementation
|
||||
of the ideas in htmx. That work gave me a chance to get a lot more familiar with the `fetch()` and, especially, the
|
||||
[async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) infrastructure
|
||||
available in JavaScript.
|
||||
|
||||
In doing that work I began to wonder if that, while the htmx [API](https://htmx.org/reference/#attributes)
|
||||
is (at least reasonably) correct, maybe there was room for a more dramatic change of the implementation that took
|
||||
advantage of these features in order to simplify the library.
|
||||
|
||||
Further, changing from ye olde [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
|
||||
(a legacy of htmx 1.0 IE support) to [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) would
|
||||
be a pretty violent change, guaranteed to break at least *some* stuff.
|
||||
|
||||
So I began thinking: if we are going to consider moving to fetch, then maybe we should _also_ use this update as a
|
||||
chance address at least _some_ of the [quirks & cruft](https://htmx.org/quirks/) that htmx has acquired over its lifetime.
|
||||
|
||||
So, eventually & reluctantly, I have changed my mind: there _will_ be another major version of htmx.
|
||||
|
||||
However, in order to keep my word that there will not be a htmx 3.0, the next release will instead be htmx 4.0.
|
||||
|
||||
## Project Goals
|
||||
|
||||
With htmx 4.0 we are rebuilding the internals of htmx, based on the lessons learned from
|
||||
fixi.js and [five+ years](https://www.npmjs.com/package/htmx.org/v/0.0.1) of supporting htmx.
|
||||
|
||||
There are three major simplifying changes:
|
||||
|
||||
### The fetch()ening
|
||||
|
||||
The biggest internal change is that `fetch()` will replace `XMLHttpRequest` as the core ajax infrastructure. This
|
||||
won't actually have a huge effect on most usages of htmx _except_ that the events model will necessarily change due
|
||||
to the differences between `fetch()` and `XMLHttpRequest`.
|
||||
|
||||
### Explicit Inheritance By Default
|
||||
|
||||
I feel that the biggest mistake in htmx 1.0 & 2.0 was making attribute inheritance implicit. I was inspired by CSS in
|
||||
doing this, and the results have been roughly the same as CSS: powerful & maddening.
|
||||
|
||||
In htmx 4.0, attribute inheritance will be explicit by default rather than implicit. Explicit inheritance will
|
||||
be done via the `:inherited` modifier:
|
||||
|
||||
```html
|
||||
<div hx-target:inherited="#output">
|
||||
<button hx-post="/up">Like</button>
|
||||
<button hx-post="/down">Dislike</button>
|
||||
</div>
|
||||
<output id="output">Pick a button...</output>
|
||||
```
|
||||
|
||||
Here the `hx-target` attribute is explicitly declared as `inherited` on the enclosing `div` and, if it wasn't, the
|
||||
`button` elements would not inherit the target from it.
|
||||
|
||||
You will be able to revert to htmx 2.0 implicit inheritance behavior via a configuration variable.
|
||||
|
||||
### No Locally Cached History
|
||||
|
||||
Another source of pain for both us and for htmx users is history support. htmx 2.0 stores history in local
|
||||
cache to make navigation faster. Unfortunately, snapshotting the DOM is often brittle because of third-party
|
||||
modifications, hidden state, etc. There is a terrible simplicity to the web 1.0 model of blowing everything away and
|
||||
starting over. There are also security concerns storing history information in session storage.
|
||||
|
||||
In htmx 2.0, we often end up recommending that people facing history-related issues simply disable the cache entirely,
|
||||
and that usually fixes the problems.
|
||||
|
||||
In htmx 4.0, history support will no longer snapshot the DOM and keep it locally. It will, rather, issue a network
|
||||
request for the restored content. This is the behavior of 2.0 on a history cache-miss, and it works reliably with
|
||||
little effort on behalf of htmx users.
|
||||
|
||||
We will offer an extension that enables history caching like in htmx 2.0, but it will be opt-in, rather than the default.
|
||||
|
||||
This tremendously simplifies the htmx codebase and should make the out-of-the-box behavior much more plug-and-play.
|
||||
|
||||
## What Stays The Same?
|
||||
|
||||
Most things.
|
||||
|
||||
The [core](https://dl.acm.org/doi/10.1145/3648188.3675127) functionality of htmx will remain the same, `hx-get`, `hx-post`,
|
||||
`hx-target`, `hx-boost`, `hx-swap`, `hx-trigger`, etc.
|
||||
|
||||
With a few configuration tweaks, most htmx 2.x based applications should work with htmx 4.x.
|
||||
|
||||
These changes will make the long term maintenance & sustainability of the project much stronger. It will also take
|
||||
pressure off the 2.0 releases, which can now focus on stability rather than contemplating new features.
|
||||
|
||||
## Upgrading
|
||||
|
||||
htmx 2.0 users _will_ face an upgrade project when moving to 4.0 in a way that they did not have to in moving
|
||||
from 1.0 to 2.0.
|
||||
|
||||
I am sorry about that, and want to offer two things to address it:
|
||||
|
||||
* htmx 2.0 (like htmx 1.0 & intercooler.js 1.0) will be supported _in perpetuity_, so there is absolutely _no_ pressure to
|
||||
upgrade your application: if htmx 2.0 is satisfying your hypermedia needs, you can stick with it.
|
||||
* We will roll htmx 4.0 out slowly, over a multi-year period. As with the htmx 1.0 -> 2.0 upgrade, there will be a long
|
||||
period where htmx 2.x is `latest` and htmx 4.x is `next`
|
||||
|
||||
## New Features
|
||||
|
||||
Beyond simplifying the implementation of htmx significantly, switching to fetch also gives us the opportunity to add
|
||||
some nice new features to htmx
|
||||
|
||||
### Streaming Responses & SSE in Core
|
||||
|
||||
By switching to `fetch()`, we can take advantage of its support for
|
||||
[readable streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams), which
|
||||
allow for a stream of content to be swapped into the DOM, rather than a single response.
|
||||
|
||||
htmx 1.0 had Server Sent Event support integrated into the library. In htmx 2.0 we pulled this functionality out as an
|
||||
extension. It turns out that SSE is just a specialized version of a streaming response, so in adding streaming
|
||||
support, it's an almost-free free two-fer to add that back into core as well.
|
||||
|
||||
This will make incremental response swapping much cleaner and well-supported in htmx.
|
||||
|
||||
### Morphing Swap in Core
|
||||
|
||||
[Three years ago](https://github.com/bigskysoftware/idiomorph/commit/7760e89d9f198b43aa7d39cc4f940f606771f47b) I had
|
||||
an idea for a DOM morphing algorithm that improved on the initial algorithm pioneered by [morphdom](https://github.com/patrick-steele-idem/morphdom).
|
||||
|
||||
The idea was to use "id sets" to make smarter decisions regarding which nodes to preserve and which nodes to delete when
|
||||
merging changes into the DOM, and I called this idea "idiomorph". Idiomorph has gone on to be adopted by many other
|
||||
web project such as [Hotwire](https://hotwired.dev/).
|
||||
|
||||
We strongly considered including it in htmx 2.0, but I decided not too because it worked well as an extension and
|
||||
htmx 2.0 had already grown larger than I wanted.
|
||||
|
||||
In 4.0, with the complexity savings we achieved by moving to `fetch()`, we can now comfortably fit a `morphInner` and
|
||||
`morphOuter` swap into core, thanks to the excellent work of Michael West.
|
||||
|
||||
### Explicit <htmx-partial> Tag Support
|
||||
|
||||
htmx has, since very early on, supported a concept of "Out-of-band" swaps: content that is removed from the main HTML
|
||||
response and swapped into the DOM elsewhere. I have always been a bit ambivalent about them, because they move away
|
||||
from [Locality of Behavior](https://htmx.org/essays/locality-of-behaviour/), but there is no doubt that they are useful
|
||||
and often crucial for achieving certain UI patterns.
|
||||
|
||||
Out-of-band swaps started off very simply: if you marked an element as `hx-swap-oob='true'`, htmx would swap the element
|
||||
as the outer HTML of any existing element already in the DOM with that id. Easy-peasy.
|
||||
|
||||
However, over time, people started asking for different functionality around Out-of-band swaps: prepending, appending,
|
||||
etc. and the feature began acquiring some fairly baroque syntax to handle all these needs.
|
||||
|
||||
We have come to the conclusion that the problem is that there are really _two_ use cases, both currently trying to be
|
||||
filled by Out-of-band swaps:
|
||||
|
||||
* A simple, id-based replacement
|
||||
* A more elaborate swap of partial content
|
||||
|
||||
Therefore, we are introducing the notion of `<htmx-partial>`s in htmx 4.0
|
||||
|
||||
A partial element is, under the covers, a template element and, thus, can contain any sort of content you like. It
|
||||
specifies on itself all the standard htmx options regarding swapping, `hx-target` and `hx-swap` in particular, allowing
|
||||
you full access to all the standard swapping behavior of htmx without using a specialized syntax. This tremendously
|
||||
simplifies the mental model for these sorts of needs, and dovetails well with the streaming support we intend to offer.
|
||||
|
||||
Out-of-band swaps will be retained in htmx 4.0, but will go back to their initial, simple focus of simply replacing
|
||||
an existing element by id.
|
||||
|
||||
### Improved View Transitions Support
|
||||
|
||||
htmx 2.0 has had [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API) support since
|
||||
[April of 2023](https://github.com/bigskysoftware/htmx/blob/master/CHANGELOG.md#190---2023-04-11). In the interceding
|
||||
two years, support for the feature has grown across browsers (c'mon, safari, you can do it) and we've gained experience
|
||||
with the feature.
|
||||
|
||||
One thing that has become apparent to us while using them is that, to use them in a stable manner, it is important
|
||||
to establish a _queue_ of transitions, so each can complete before the other begins. If you don't do this, you can get
|
||||
visually ugly transition cancellations.
|
||||
|
||||
So, in htmx 4.0 we have added this queue which will ensure that all view transitions complete smoothly.
|
||||
|
||||
CSS transitions will continue to work as before as well, although the swapping model is again made much simpler by the
|
||||
async runtime.
|
||||
|
||||
We may enable View Transitions by default, the jury is still out on that.
|
||||
|
||||
### Stabilized Event Ordering
|
||||
|
||||
A wonderful thing about `fetch()` and the async support in general is that it is _much_ easier to guarantee a stable
|
||||
order of events. By linearizing asynchronous code and allowing us to use standard language features like try/catch,
|
||||
the event model of htmx should be much more predictable and comprehensible.
|
||||
|
||||
We are going to adopt a new standard for event naming to make things even clearer:
|
||||
|
||||
`htmx:<phase>:<system>[:<optional-sub-action>]`
|
||||
|
||||
So, for example, `htmx:before:request` will be triggered before a request is made.
|
||||
|
||||
### Improved Extension Support
|
||||
|
||||
Another opportunity we have is to take advantage of the `async` behavior of `fetch()` for much better performance in our
|
||||
preload extension (where we issue a speculative (`GET` only!) request in anticipation of an actual trigger). We have
|
||||
also added an optimistic update extension to the core extensions, again made easy by the new async features.
|
||||
|
||||
In general, we have opened up the internals of the htmx request/response/swap cycle much more fully to extension developers,
|
||||
up to and including allowing them to replace the `fetch()` implementation used by htmx for a particular request. There
|
||||
should not be a need for any hacks to get the behavior you want out of htmx now: the events and the open "context" object
|
||||
should provide the ability to do almost anything.
|
||||
|
||||
### Improved `hx-on` Support
|
||||
|
||||
In htmx 2.0, I somewhat reluctantly added the [`hx-on`](https://htmx.org/attributes/hx-on/) attributes to support light
|
||||
scripting inline on elements. I added this because HTML does not allow you to listen for arbitrary events via `on`
|
||||
attributes: only standard DOM events like `onclick` can be responded to.
|
||||
|
||||
We hemmed and hawed about the syntax and so, unfortunately, there are a few different ways to do it.
|
||||
|
||||
In htmx 4.0 we will adopt a single standard for the `hx-on` attributes: `hx-on:<event name>`. Additionally, we are
|
||||
working to improve the htmx JavaScript API (especially around async operation support) and will make those features
|
||||
available in `hx-on`:
|
||||
|
||||
```html
|
||||
<button hx-post="/like"
|
||||
hx-on:htmx:after:swap="await timeout('3s'); ctx.newContent[0].remove()">
|
||||
Get A Response Then Remove It 3 Seconds Later
|
||||
</button>
|
||||
```
|
||||
|
||||
htmx will never support a fully featured scripting mechanism in core, we recommend something like
|
||||
[Alpine.js](https://alpinejs.dev/) for that, but our hope is that we can provide a relatively minimalist API that
|
||||
allows for easy, light async scripting of the DOM.
|
||||
|
||||
I should note that htmx 4.0 will continue to work with `eval()` disabled, but you will need to forego a few features like
|
||||
`hx-on` if you choose to do so.
|
||||
|
||||
### A Better But Familiar htmx
|
||||
|
||||
All in all, our hope is that htmx 4.0 will feel an awful lot like 2.0, but with better features and, we hope, with fewer bugs.
|
||||
|
||||
## Timeline
|
||||
|
||||
As always, software takes as long as it takes.
|
||||
|
||||
However, our current planned timeline is:
|
||||
|
||||
* An alpha release is available _today_: `htmx@4.0.0-alpha1`
|
||||
* A 4.0.0 release should be available in early-to-mid 2026
|
||||
* 4.0 will be marked `latest` in early-2027ish
|
||||
|
||||
You can track our progress (and see quite a bit of dust flying around) in the `four` branch on
|
||||
[github](https://github.com/bigskysoftware/htmx/tree/four) and at:
|
||||
|
||||
<https://four.htmx.org>
|
||||
|
||||
Thank you for your patience and pardon our dust!
|
||||
|
||||
> "Well, when events change, I change my mind. What do you do?" --Paul Samuelson or John Maynard Keynes
|
||||
@@ -130,6 +130,10 @@ htmx extensions are split into two categories:
|
||||
<td>{% markdown() %} [dynamic-url](https://github.com/FumingPower3925/htmx-dynamic-url/blob/main/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} Allows dynamic URL path templating using `{varName}` placeholders, resolved via configurable custom function or `window.` fallback. It does not rely on `hx-vals`. Useful when needing to perform requests to paths that depend on application state. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [optimistic]([https://github.com/FumingPower3925/htmx-dynamic-url](https://github.com/lorenseanstewart/hx-optimistic/blob/main/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension provides a way to optimistically update the UI to increase perceived performance {% end %}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr><th scope="rowgroup" colspan="2">Data API</th></tr>
|
||||
|
||||
BIN
www/static/img/fetch.png
Normal file
BIN
www/static/img/fetch.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 375 KiB |
BIN
www/static/img/paris-olympics-progress-bar.png
Normal file
BIN
www/static/img/paris-olympics-progress-bar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
BIN
www/static/img/rsz_instant_famous.png
Normal file
BIN
www/static/img/rsz_instant_famous.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
www/static/img/stake.jpeg
Normal file
BIN
www/static/img/stake.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Reference in New Issue
Block a user