mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-28 05:21:18 +00:00
Merge branch 'master' into dev
This commit is contained in:
commit
63016891a6
189
www/content/QUIRKS.md
Normal file
189
www/content/QUIRKS.md
Normal file
@ -0,0 +1,189 @@
|
||||
+++
|
||||
title = "htmx quirks"
|
||||
date = 2024-12-23
|
||||
updated = 2024-12-23
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
This is a "quirks" page, based on [SQLite's "Quirks, Caveats, and Gotchas In SQLite" page](https://www.sqlite.org/quirks.html).
|
||||
|
||||
## Attribute Inheritance
|
||||
|
||||
Many attributes in htmx are [inherited](@/docs.md#inheritance): child elements can receive behavior from attributes located
|
||||
on parent elements.
|
||||
|
||||
As an example, here are two htmx-powered buttons that inherit their [target](@/attributes/hx-target.md) from a parent
|
||||
div:
|
||||
|
||||
```html
|
||||
<div hx-target="#output">
|
||||
<button hx-post="/items/100/like">Like</button>
|
||||
<button hx-delete="/items/100">Delete</button>
|
||||
</div>
|
||||
<output id="output"></output>
|
||||
```
|
||||
|
||||
This helps avoid repeating attributes, thus keeping code [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).
|
||||
|
||||
On the other hand, as the attributes get further away elements, you lose [Locality of Behavior](@/essays/locality-of-behaviour.md)
|
||||
and it becomes more difficult to understand what an element is doing.
|
||||
|
||||
It is also possible to inadvertently change the behavior of elements by adding attributes to parents.
|
||||
|
||||
Some people prefer to disable inheritance in htmx entirely, using the `htmx.config.disableInheritance`
|
||||
[configuration variable](@/docs.md#config).
|
||||
|
||||
Here is a `meta` tag configuration that does so:
|
||||
|
||||
```html
|
||||
<meta name="htmx-config" content='{"disableInheritance":true}'>
|
||||
```
|
||||
|
||||
## The Default Swap Strategy is `innerHTML`
|
||||
|
||||
The [`hx-swap`](@/attributes/hx-swap.md) attribute allows you to control how a swap is performed. The default strategy is
|
||||
`innerHTML`, that is, to place the response HTML content within the target element.
|
||||
|
||||
Many people prefer to use the `outerHTML` strategy as the default instead.
|
||||
|
||||
You can change this behavior using the `htmx.config.defaultSwapStyle`
|
||||
[configuration variable](@/docs.md#config).
|
||||
|
||||
Here is a `meta` tag configuration that does so:
|
||||
|
||||
```html
|
||||
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
|
||||
```
|
||||
|
||||
## Targeting the `body` Always Performs an innerHTML Swap
|
||||
|
||||
For historical reasons, if you target the `body` element, htmx will
|
||||
[always perform an `innerHTML` swap](https://github.com/bigskysoftware/htmx/blob/fb78106dc6ef20d3dfa7e54aca20408c4e4336fc/src/htmx.js#L1696).
|
||||
|
||||
This means you cannot change attributes on the `body` tag via an htmx request.
|
||||
|
||||
## By Default `4xx` & `5xx` Responses Do Not Swap
|
||||
|
||||
htmx has never swapped "error" status response codes (`400`s & `500`s) by default.
|
||||
|
||||
This behavior annoys some people, and some server frameworks, in particular, will return a `422 - Unprocessable Entity`
|
||||
response code to indicate that a form was not filled out properly.
|
||||
|
||||
This can be very confusing when it is first encountered.
|
||||
|
||||
You can configure the response behavior of htmx via the [`htmx:beforeSwap`](@/docs.md#modifying_swapping_behavior_with_events)
|
||||
event or [via the `htmx.config.responseHandling` config array](https://htmx.org/docs/#response-handling).
|
||||
|
||||
Here is the default configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"responseHandling": [
|
||||
{"code":"204", "swap": false},
|
||||
{"code":"[23]..", "swap": true},
|
||||
{"code":"[45]..", "swap": false, "error":true},
|
||||
{"code":"...", "swap": false}]
|
||||
}
|
||||
```
|
||||
|
||||
Note that `204 No Content` also is not swapped.
|
||||
|
||||
If you want to swap everything regardless of response code, you can use this configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"responseHandling": [
|
||||
{"code":"...", "swap": true}]
|
||||
}
|
||||
```
|
||||
|
||||
If you want to specifically allow `422` responses to swap, you can use this configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"responseHandling": [
|
||||
{"code":"422", "swap": true},
|
||||
{"code":"204", "swap": false},
|
||||
{"code":"[23]..", "swap": true},
|
||||
{"code":"[45]..", "swap": false, "error":true},
|
||||
{"code":"...", "swap": false}]
|
||||
}
|
||||
```
|
||||
|
||||
Here is a meta tag allowing all responses to swap:
|
||||
|
||||
```html
|
||||
<meta name="htmx-config" content='{"responseHandling": [{"code":"...", "swap": true}]}'>
|
||||
```
|
||||
|
||||
## `GET` Requests on Non-Form Elements Do Not Include Form Values by Default
|
||||
|
||||
If a non-form element makes a non-`GET` request (e.g. a `PUT` request) via htmx, the values of the enclosing form
|
||||
of that element (if any) [will be included in the request](@/docs.md#parameters).
|
||||
|
||||
However, if the element issues a `GET`, the values of an enclosing form will
|
||||
[not be included.](https://github.com/bigskysoftware/htmx/blob/fb78106dc6ef20d3dfa7e54aca20408c4e4336fc/src/htmx.js#L3525)
|
||||
|
||||
If you wish to include the values of the enclosing form when issuing an `GET` you can use the
|
||||
[`hx-include`](@/attributes/hx-include.md) attribute like so:
|
||||
|
||||
```html
|
||||
<button hx-get="/search"
|
||||
hx-include="closest form">
|
||||
Search
|
||||
</button>
|
||||
```
|
||||
|
||||
## History Can Be Tricky
|
||||
|
||||
htmx provides support for interacting with the browser's [history](@/docs.md#history). This can be very powerful, but it
|
||||
can also be tricky, particularly if you are using 3rd party JavaScript libraries that modify the DOM.
|
||||
|
||||
There can also be [security concerns](@/docs.md#hx-history) when using htmx's history support.
|
||||
|
||||
Most of these issues can be solved by disabling any local history cache and simply issuing a server request when a
|
||||
user navigates backwards in history, with the tradeoff that history navigation will be slower.
|
||||
|
||||
Here is a meta tag that disables history caching:
|
||||
|
||||
```html
|
||||
<meta name="htmx-config" content='{"historyCacheSize": 0}'>
|
||||
```
|
||||
|
||||
## Some People Don't Like `hx-boost`
|
||||
|
||||
[`hx-boost`](@/attributes/hx-boost.md) is an odd feature compared with most other aspects of htmx: it "magically" turns
|
||||
all anchor tags and forms into AJAX requests.
|
||||
|
||||
This can speed the feel of these interactions up, and also allows the forms and anchors to continue working when
|
||||
[JavaScript is disabled](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement), however it comes
|
||||
with some tradeoffs:
|
||||
|
||||
* The history issues mentioned above can show up
|
||||
* Only the body of the web page will be updated, so any styles and scripts in the new page `head` tag will be discarded
|
||||
* The global javascript scope is not refreshed, so it is possible to have strange interactions between pages. For example
|
||||
a global `let` may start failing because a symbol is already defined.
|
||||
|
||||
Some members on the core htmx team feel that, due to these issues, as well as the fact that browsers have improved
|
||||
quite a bit in page navigation, it is best to avoid `hx-boost` and
|
||||
[just use unboosted links and forms](https://unplannedobsolescence.com/blog/less-htmx-is-more/).
|
||||
|
||||
There is no doubt that `hx-boost` is an odd-man out when compared to other htmx attributes and suffers from the dictum
|
||||
that "If something magically works, then it can also magically break."
|
||||
|
||||
Despite this fact, I (Carson) still feel it is useful in many situations, and it is used on the <https://htmx.org>
|
||||
website.
|
||||
|
||||
## The JavaScript API Is Not A Focus
|
||||
|
||||
htmx is a hypermedia-oriented front end library. This means that htmx enhances HTML via
|
||||
[attributes](@/reference.md#attributes) in the HTML , rather than providing an elaborate
|
||||
JavaScript API.
|
||||
|
||||
There _is_ a [JavaScript API](@/reference.md#api), but it is not a focus of the library and, in most cases,
|
||||
should not be used heavily by htmx end users.
|
||||
|
||||
If you find yourself using it heavily, especially the [`htmx.ajax()`](@/api.md#ajax) method, it may be
|
||||
worth asking yourself if there is a more htmx-ish approach to achieve what you are doing.
|
@ -1,5 +1,5 @@
|
||||
+++
|
||||
insert_anchor_links = "left"
|
||||
insert_anchor_links = "heading"
|
||||
+++
|
||||
|
||||
<style type="text/css">
|
||||
@ -52,13 +52,13 @@ body.ads .ad img {
|
||||
</style>
|
||||
<script type="application/javascript">
|
||||
if(window.location.search=="?wuw=true" || window.location.search=="?suw=true") {
|
||||
document.body.classList.add('lmao');
|
||||
document.body.classList.add("lmao")
|
||||
}
|
||||
if(window.location.search=="?uwu=true") {
|
||||
document.body.classList.add('kawaii');
|
||||
document.body.classList.add("kawaii")
|
||||
}
|
||||
if(window.location.search=="?ads=true") {
|
||||
document.body.classList.add('ads');
|
||||
document.body.classList.add("ads")
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -69,7 +69,6 @@ if(window.location.search=="?ads=true") {
|
||||
</div>
|
||||
|
||||
<div class="dark-hero full-width" classes="add appear">
|
||||
<marquee>
|
||||
<div class="main">
|
||||
<span class="logo dark"><<span class="blue">/</span>> <span class="no-mobile">htm<span class="blue">x</span></span></span>
|
||||
<sub class="no-mobile"><i>high power tools for HTML</i></sub>
|
||||
@ -85,7 +84,6 @@ if(window.location.search=="?ads=true") {
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</marquee>
|
||||
</div>
|
||||
<div class="ad">
|
||||
<a href="https://swag.htmx.org">
|
||||
@ -185,186 +183,163 @@ Thank you to all our generous <a href="https://github.com/sponsors/bigskysoftwar
|
||||
<div style="overflow-x: auto">
|
||||
|
||||
<h1 style="margin-top:32px;text-align:center">Platinum Sponsor</h1>
|
||||
<table id="sponsor-table">
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<div style="display: grid;grid-template-columns: 1fr">
|
||||
<div>
|
||||
<a data-github-account="commspace" href="https://www.commspace.co.za">
|
||||
<img class="dark-hidden" src="/img/commspace.svg" alt="commspace" style="min-width:200px"/>
|
||||
<img class="dark-visible" src="/img/commspace-dark.svg" alt="commspace" style="min-width:200px"/>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Gold Sponsors
|
||||
|
||||
<table id="sponsor-table">
|
||||
<tr>
|
||||
<td>
|
||||
<a data-github-account="NotASithLord" href="https://hydrahost.com">
|
||||
<img class="dark-hidden" src="/img/hydra-hosting.svg" alt="The GPU Marketplace" style="width:100%;">
|
||||
<img class="dark-visible" src="/img/hydra-hosting-dark.svg" alt="The GPU Marketplace" style="width:100%;">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a data-github-account="deco-cx" href="https://deco.cx/?utm_source=htmx"><img src="/img/deco.cx-logo-outline.png" alt="Your first and last web editor" style="width:100%;"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Silver Sponsors
|
||||
|
||||
<table id="sponsor-table">
|
||||
<tr>
|
||||
<td>
|
||||
<a data-github-account="JetBrainsOfficial" href="https://www.jetbrains.com"><img src="/img/jetbrains.svg" alt="Jetbrains" style="max-width:80%;min-width:100px;"></a>
|
||||
</td>
|
||||
<td>
|
||||
<style>
|
||||
#silver-sponsors div {
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
}
|
||||
#silver-sponsors div a * {
|
||||
width: 80%;
|
||||
}
|
||||
#silver-sponsors img {
|
||||
min-width: 150px;
|
||||
}
|
||||
</style>
|
||||
<div id="silver-sponsors" style="display: grid;grid-template-columns: repeat(3, 1fr); align-items: center; justify-items: center; ">
|
||||
<div>
|
||||
<a data-github-account="JetBrainsOfficial" href="https://www.jetbrains.com"><img src="/img/jetbrains.svg" alt="Jetbrains"></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://github.blog/2023-04-12-github-accelerator-our-first-cohort-and-whats-next"><img class="dark-invert" src="/img/Github_Logo.png" alt="GitHub" style="max-width:80%;min-width:100px;"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table id="sponsor-table">
|
||||
<tr>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="craftcms" href="https://craftcms.com">
|
||||
<img class="dark-hidden" src="/img/logo-craft-cms.svg" alt="craft cms" style="width:90%;max-width:200px">
|
||||
<img class="dark-visible" src="/img/logo-craft-cms-dark.svg" alt="craft cms" style="width:90%;max-width:200px">
|
||||
<img class="dark-hidden" src="/img/logo-craft-cms.svg" alt="craft cms">
|
||||
<img class="dark-visible" src="/img/logo-craft-cms-dark.svg" alt="craft cms">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="ButterCMS" href="https://buttercms.com/?utm_campaign=sponsorship&utm_medium=banner&utm_source=htmxhome">
|
||||
<img class="dark-invert" src="/img/butter-cms.svg" alt="ButterCMS" style="width:100%;max-width:200px">
|
||||
<img class="dark-invert" style="min-width: 150px;" src="/img/butter-cms.svg" alt="ButterCMS">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="Black-HOST" href="https://black.host">
|
||||
<img class="dark-invert" src="/img/blackhost-logo.svg" alt="Black Host" style="width:100%;max-width:200px">
|
||||
<img class="dark-invert" src="/img/blackhost-logo.svg" alt="Black Host">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.v7n.com/">
|
||||
<img alt="V7N" class="dark-hidden" src="/img/v7n-logo.png" style="width:100%;max-width:200px">
|
||||
<img alt="V7N" class="dark-visible" src="/img/v7n-logo-dark.png" style="width:100%;max-width:200px">
|
||||
<img alt="V7N" class="dark-hidden" src="/img/v7n-logo.png">
|
||||
<img alt="V7N" class="dark-visible" src="/img/v7n-logo-dark.png">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a data-github-account="sekunho" href="https://twitter.com/sekunho_/"><img src="/img/sekun-doggo.jpg" alt="Hiro The Doggo" style="border: 2px solid lightgray; border-radius:20px; width:100%;max-width:150px"></a>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="sekunho" href="https://twitter.com/sekunho_/"><img src="/img/sekun-doggo.jpg" alt="Hiro The Doggo" style="border: 2px solid lightgray; border-radius:20px"></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://dasfilter.shop/pages/affiliates">
|
||||
<img class="dark-hidden" alt="Das Filter" src="/img/das-filter.svg" style="width:100%;max-width:300px">
|
||||
<img class="dark-visible" alt="Das Filter" src="/img/das-filter-dark.svg" style="width:100%;max-width:300px">
|
||||
<img class="dark-hidden" alt="Das Filter" src="/img/das-filter.svg">
|
||||
<img class="dark-visible" alt="Das Filter" src="/img/das-filter-dark.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.pullapprove.com/?utm_campaign=sponsorship&utm_medium=banner&utm_source=htmx">
|
||||
<img class="dark-hidden" src="/img/pullapprove-logo.svg" alt="PullApprove" style="width:100%;max-width:200px"/>
|
||||
<img class="dark-visible" src="/img/pullapprove-logo-dark.svg" alt="PullApprove" style="width:100%;max-width:200px"/>
|
||||
<img class="dark-hidden" src="/img/pullapprove-logo.svg" alt="PullApprove"/>
|
||||
<img class="dark-visible" src="/img/pullapprove-logo-dark.svg" alt="PullApprove"/>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="transloadit" href=" https://transloadit.com/?utm_source=htmx&utm_medium=referral&utm_campaign=sponsorship&utm_content=website/">
|
||||
<img class="dark-hidden" alt="Transloadit" src="/img/logos-transloadit-default.svg" style="width:100%;max-width:200px">
|
||||
<img class="dark-visible" alt="Transloadit" src="/img/transloadit-logo-dark.svg" style="width:100%;max-width:200px">
|
||||
<img class="dark-hidden" alt="Transloadit" src="/img/logos-transloadit-default.svg">
|
||||
<img class="dark-visible" alt="Transloadit" src="/img/transloadit-logo-dark.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="uibakery" href="https://uibakery.io">
|
||||
<img class="dark-hidden" src="/img/ui-bakery.svg" alt="UI Bakery" style="width:100%;max-width:250px">
|
||||
<img class="dark-visible" src="/img/ui-bakery-dark.svg" alt="UI Bakery" style="width:100%;max-width:250px"></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img class="dark-hidden" src="/img/ui-bakery.svg" alt="UI Bakery">
|
||||
<img class="dark-visible" src="/img/ui-bakery-dark.svg" alt="UI Bakery"></a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a data-github-account="tracebit-com" href="https://tracebit.com/?utm_source=htmx">
|
||||
<img class="dark-hidden" alt="Tracebit Cloud Canaries" src="/img/tracebit-logo.png" style="width:100%;max-width:250px">
|
||||
<img class="dark-visbile" alt="Tracebit Cloud Canaries" src="/img/tracebit-logo-dark.png" style="width:100%;max-width:250px">
|
||||
<img class="dark-hidden" alt="Tracebit Cloud Canaries" src="/img/tracebit-logo.png">
|
||||
<img class="dark-visbile" alt="Tracebit Cloud Canaries" src="/img/tracebit-logo-dark.png">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="pubkey" href="https://rxdb.info/?utm_source=sponsor&utm_medium=githubsponsor&utm_campaign=githubsponsor-htmx">
|
||||
<img src="/img/rxdb.svg" alt="RxDB JavaScript Database" style="width:100%;max-width:150px"></a>
|
||||
</td>
|
||||
<td>
|
||||
<img src="/img/rxdb.svg" alt="RxDB JavaScript Database"></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.ohne-makler.net/"><img src="/img/ohne-makler.svg" alt="Ohne-Makler" style="width:100%;max-width:150px"></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<a data-github-account="cased" href="https://cased.com///">
|
||||
<img class="dark-hidden" alt="Developer friendly DevOps" src="/img/Cased_Logo_DarkBlue.svg" style="width:100%;max-width:250px">
|
||||
<img class="dark-visible" alt="Developer friendly DevOps" src="/img/Cased_Logo_Beige-01.svg" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a data-github-account="apesternikov" href="https://codereviewbot.ai/">
|
||||
<img class="dark-hidden" alt="AI Code Review Bot" src="/img/codereviewbot.svg" style="width:100%;max-width:250px">
|
||||
<img class="dark-visible" alt="AI Code Review Bot" src="/img/codereviewbot-dark.svg" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="llcorg" href="https://www.llc.org/">
|
||||
<img alt="How to start an LLC - a guide from LLC.org, proud open source sponsor" src="/img/llc-org.svg" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<a data-github-account="VPSServerCom" href="https://www.vpsserver.com/">
|
||||
<img class="dark-hidden" alt="VPS Server Hosting in the Cloud: Cost Efficiency" src="/img/vps-server-logo.svg" style="width:100%;max-width:250px">
|
||||
<img class="dark-visible" alt="VPS Server Hosting in the Cloud: Cost Efficiency" src="/img/vps-server-logo-dark.svg" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="appleple" href="https://www.a-blogcms.jp/">
|
||||
<img src="/img/ablogcms_logo.svg" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="CoverageCritic" alt="Find Internet Providers With Broadband Map" href="https://broadbandmap.com/">
|
||||
<img class="dark-hidden" src="/img/BroadbandMapLogo2LineLightMode.png" style="width:100%;max-width:250px">
|
||||
<img class="dark-visible" src="/img/BroadbandMapLogo2LineDarkMode.png" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<a data-github-account="upstatebreaker" href="https://buymybreaker.com/">
|
||||
<img class="dark-hidden" alt="Electrical Equipment - BuyMyBreaker.com" src="/img/bmb-light.svg" style="width:100%;max-width:50px" >
|
||||
<img class="dark-visible" alt="Electrical Equipment - BuyMyBreaker.com" src="/img/bmb-dark.svg" style="width:100%;max-width:50px">
|
||||
<img class="dark-hidden" alt="Electrical Equipment - BuyMyBreaker.com" src="/img/bmb-light.svg" style="min-width: 80px" >
|
||||
<img class="dark-visible" alt="Electrical Equipment - BuyMyBreaker.com" src="/img/bmb-dark.svg" style="min-width: 80px">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="Viralyft" alt="Buy YouTube views" href="https://viralyft.com/buy-youtube-views/">
|
||||
<img class="dark-hidden" src="/img/Viralyft_light.png" style="width:100%;max-width:250px">
|
||||
<img class="dark-visible" src="/img/Viralyft_dark.png" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a data-github-account="radioplusexperts" alt="Assignment Writing service" href="https://edubirdie.com/do-my-assignment">
|
||||
<img class="dark-hidden" src="/img/edubirdie-light.png" style="width:100%;max-width:250px">
|
||||
<img class="dark-visible" src="/img/edubirdie-dark.png" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="Follower24" alt="Follower24" href="https://www.follower24.de/">
|
||||
<img class="dark-hidden" src="/img/follower_light.svg" style="width:100%;max-width:250px">
|
||||
<img class="dark-visible" src="/img/follower_dark.svg" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<a data-github-account="ExchangeRate-API" alt="The Accurate & Reliable Exchange Rate API" href="https://www.exchangerate-api.com">
|
||||
<img class="dark-hidden" src="/img/exchange-rate-api.png" style="width:100%;max-width:250px">
|
||||
<img class="dark-visible" src="/img/exchange-rate-api-dark.png" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a data-github-account="radioplusexperts" alt="Assignment Writing service" href="https://edubirdie.com/do-my-assignment">
|
||||
<img class="dark-hidden" src="/img/edubirdie-light.png" style="width:100%;max-width:250px">
|
||||
<img class="dark-visible" src="/img/edubirdie-dark.png" style="width:100%;max-width:250px">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center;font-style: italic;margin-top: 26px;">ʕ •ᴥ•ʔ made in montana</div>
|
||||
|
@ -1,5 +1,9 @@
|
||||
+++
|
||||
title = "Javascript API"
|
||||
description = """\
|
||||
This documentation describes the JavaScript API for htmx, including methods and properties for configuring the \
|
||||
behavior of htmx, working with CSS classes, AJAX requests, event handling, and DOM manipulation. The API provides \
|
||||
helper functions primarily intended for extension development and event management."""
|
||||
+++
|
||||
|
||||
While it is not a focus of the library, htmx does provide a small API of helper methods, intended mainly for [extension development](https://htmx.org/extensions) or for working with [events](@/events.md).
|
||||
|
@ -1,5 +1,9 @@
|
||||
+++
|
||||
title = "hx-boost"
|
||||
description = """\
|
||||
The hx-boost attribute in htmx enables progressive enhancement by converting standard HTML anchors and forms into \
|
||||
AJAX requests, maintaining graceful fallback for users without JavaScript while providing modern dynamic page \
|
||||
updates for those with JavaScript enabled."""
|
||||
+++
|
||||
|
||||
The `hx-boost` attribute allows you to "boost" normal anchors and form tags to use AJAX instead. This
|
||||
|
@ -1,5 +1,9 @@
|
||||
+++
|
||||
title = "hx-confirm"
|
||||
description = """\
|
||||
The hx-confirm attribute in htmx provides a way to add confirmation dialogs before executing requests, allowing \
|
||||
you to protect users from accidental destructive actions. This documentation explains how to implement confirmation \
|
||||
prompts and customize their behavior through event handling."""
|
||||
+++
|
||||
|
||||
The `hx-confirm` attribute allows you to confirm an action before issuing a request. This can be useful
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-delete"
|
||||
description = """\
|
||||
The hx-delete attribute in htmx will cause an element to issue a DELETE request to the specified URL and swap the \
|
||||
returned HTML into the DOM using a swap strategy."""
|
||||
+++
|
||||
|
||||
The `hx-delete` attribute will cause an element to issue a `DELETE` to the specified URL and swap
|
||||
|
@ -1,5 +1,6 @@
|
||||
+++
|
||||
title = "hx-disable"
|
||||
description = "The hx-disable attribute in htmx will disable htmx processing for a given element and all its children."
|
||||
+++
|
||||
|
||||
The `hx-disable` attribute will disable htmx processing for a given element and all its children. This can be
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-disabled-elt"
|
||||
description = """\
|
||||
The hx-disabled-elt attribute in htmx allows you to specify elements that will have the `disabled` attribute added \
|
||||
to them for the duration of the request."""
|
||||
+++
|
||||
|
||||
The `hx-disabled-elt` attribute allows you to specify elements that will have the `disabled` attribute
|
||||
@ -16,7 +19,7 @@ added to them for the duration of the request. The value of this attribute can b
|
||||
(e.g. `next button` will disable the closest following sibling `button` element)
|
||||
* `previous` which resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling)
|
||||
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
|
||||
(e.g `previous input` will disable the closest previous sibling `input` element)
|
||||
(e.g. `previous input` will disable the closest previous sibling `input` element)
|
||||
|
||||
Here is an example with a button that will disable itself during a request:
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
+++
|
||||
title = "hx-disinherit"
|
||||
description = """\
|
||||
The hx-disinherit attribute in htmx lets you control how child elements inherit attributes from their parents. This \
|
||||
documentation explains how to selectively disable inheritance of specific htmx attributes or all attributes, \
|
||||
allowing for more granular control over your web application's behavior."""
|
||||
+++
|
||||
|
||||
The default behavior for htmx is to "inherit" many attributes automatically: that is, an attribute such as
|
||||
|
@ -1,5 +1,9 @@
|
||||
+++
|
||||
title = "hx-encoding"
|
||||
description = """\
|
||||
The hx-encoding attribute in htmx allows you to switch the request encoding from the usual \
|
||||
`application/x-www-form-urlencoded` encoding to `multipart/form-data`, usually to support file uploads in an AJAX \
|
||||
request."""
|
||||
+++
|
||||
|
||||
The `hx-encoding` attribute allows you to switch the request encoding from the usual `application/x-www-form-urlencoded`
|
||||
|
@ -1,10 +1,13 @@
|
||||
+++
|
||||
title = "hx-ext"
|
||||
description = """\
|
||||
The hx-ext attribute in htmx enables one or more htmx extensions for an element and all its children. You can also \
|
||||
use this attribute to ignore an extension that is enabled by a parent element."""
|
||||
+++
|
||||
|
||||
The `hx-ext` attribute enables an htmx [extension](https://htmx.org/extensions) for an element and all its children.
|
||||
|
||||
The value can be a single extension name or a comma separated list of extensions to apply.
|
||||
The value can be a single extension name or a comma-separated list of extensions to apply.
|
||||
|
||||
The `hx-ext` tag may be placed on parent elements if you want a plugin to apply to an entire swath of the DOM,
|
||||
and on the `body` tag for it to apply to all htmx requests.
|
||||
@ -25,4 +28,8 @@ hierarchy and it will apply to all child elements.
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```html
|
||||
<body hx-ext="preload,morph">
|
||||
"preload" and "morph" extensions are used in this part of the tree...
|
||||
</body>
|
||||
```
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-get"
|
||||
description = """\
|
||||
The hx-get attribute in htmx will cause an element to issue a GET request to the specified URL and swap the returned \
|
||||
HTML into the DOM using a swap strategy."""
|
||||
+++
|
||||
|
||||
The `hx-get` attribute will cause an element to issue a `GET` to the specified URL and swap
|
||||
@ -15,8 +18,9 @@ This example will cause the `button` to issue a `GET` to `/example` and swap the
|
||||
### Notes
|
||||
|
||||
* `hx-get` is not inherited
|
||||
* By default `hx-get` does not include any parameters. You can use the [hx-params](@/attributes/hx-params.md)
|
||||
* By default `hx-get` usually does not include any parameters. You can use the [hx-params](@/attributes/hx-params.md)
|
||||
attribute to change this
|
||||
* NB: If the element with the `hx-get` attribute also has a value, this will be included as a parameter unless explicitly removed
|
||||
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
|
||||
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
|
||||
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
|
||||
|
@ -1,24 +1,30 @@
|
||||
+++
|
||||
title = "hx-headers"
|
||||
description = """\
|
||||
The hx-headers attribute in htmx allows you to add to the headers that will be submitted with an AJAX request."""
|
||||
+++
|
||||
|
||||
The `hx-headers` attribute allows you to add to the headers that will be submitted with an AJAX request.
|
||||
The `hx-headers` attribute allows you to add to the headers that will be submitted with an AJAX request.
|
||||
|
||||
By default, the value of this attribute is a list of name-expression values in [JSON (JavaScript Object Notation)](https://www.json.org/json-en.html)
|
||||
By default, the value of this attribute is a list of name-expression values in [JSON (JavaScript Object Notation)](https://www.json.org/json-en.html)
|
||||
format.
|
||||
|
||||
If you wish for `hx-headers` to *evaluate* the values given, you can prefix the values with `javascript:` or `js:`.
|
||||
|
||||
```html
|
||||
<div hx-get="/example" hx-headers='{"myHeader": "My Value"}'>Get Some HTML, Including A Custom Header in the Request</div>
|
||||
|
||||
<div hx-get="/example" hx-headers='js:{myVal: calculateValue()}'>Get Some HTML, Including a Dynamic Custom Header from Javascript in the Request</div>
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
* By default, the value of `hx-headers` must be valid [JSON](https://developer.mozilla.org/en-US/docs/Glossary/JSON).
|
||||
* By default, the value of `hx-headers` must be valid [JSON](https://developer.mozilla.org/en-US/docs/Glossary/JSON).
|
||||
It is **not** dynamically computed. If you use the `javascript:` prefix, be aware that you are introducing
|
||||
security considerations, especially when dealing with user input such as query strings or user-generated content,
|
||||
which could introduce a [Cross-Site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/) vulnerability.
|
||||
security considerations, especially when dealing with user input such as query strings or user-generated content,
|
||||
which could introduce a [Cross-Site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/) vulnerability.
|
||||
|
||||
* Whilst far from being a foolproof solution to [Cross-Site Request Forgery](https://owasp.org/www-community/attacks/csrf), the `hx-headers` attribute can support backend services to provide [CSRF prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html). For more information see the [CSRF Prevention](https://htmx.org/docs/#csrf-prevention) section.
|
||||
|
||||
## Notes
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-history-elt"
|
||||
description = """\
|
||||
The hx-history-elt attribute in htmx allows you to specify the element that will be used to snapshot and restore \
|
||||
page state during navigation. In most cases we do not recommend using this element."""
|
||||
+++
|
||||
|
||||
The `hx-history-elt` attribute allows you to specify the element that will be used to snapshot and
|
||||
|
@ -1,5 +1,9 @@
|
||||
+++
|
||||
title = "hx-history"
|
||||
description = """\
|
||||
The hx-history attribute in htmx allows you to prevent sensitive page data from being stored in the browser's \
|
||||
localStorage cache during history navigation, ensuring that the page state is retrieved from the server instead when \
|
||||
navigating through history."""
|
||||
+++
|
||||
|
||||
Set the `hx-history` attribute to `false` on any element in the current document, or any html fragment loaded into the current document by htmx, to prevent sensitive data being saved to the localStorage cache when htmx takes a snapshot of the page state.
|
||||
|
@ -1,5 +1,6 @@
|
||||
+++
|
||||
title = "hx-include"
|
||||
description = "The hx-include attribute in htmx allows you to include additional element values in an AJAX request."
|
||||
+++
|
||||
|
||||
The `hx-include` attribute allows you to include additional element values in an AJAX request. The value of this
|
||||
@ -14,7 +15,7 @@ attribute can be:
|
||||
* `next <CSS selector>` which will scan the DOM forward for the first element that matches the given CSS selector.
|
||||
(e.g. `next .error` will target the closest following sibling element with `error` class)
|
||||
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
|
||||
(e.g `previous .error` will target the closest previous sibling with `error` class)
|
||||
(e.g. `previous .error` will target the closest previous sibling with `error` class)
|
||||
|
||||
Here is an example that includes a separate input value:
|
||||
|
||||
@ -50,4 +51,5 @@ Note that if you include a non-input element, all input elements enclosed in tha
|
||||
* A standard CSS selector resolves
|
||||
to [document.querySelectorAll](https://developer.mozilla.org/docs/Web/API/Document/querySelectorAll) and will include
|
||||
multiple elements, while the extended selectors such as `find` or `next` only return a single element at most to
|
||||
include
|
||||
include
|
||||
* `hx-include` will ignore disabled inputs
|
@ -1,5 +1,9 @@
|
||||
+++
|
||||
title = "hx-indicator"
|
||||
description = """\
|
||||
The hx-indicator attribute in htmx allows you to specify the element that will have the `htmx-request` class added \
|
||||
to it for the duration of the request. This can be used to show spinners or progress indicators while the request is \
|
||||
in flight."""
|
||||
+++
|
||||
|
||||
The `hx-indicator` attribute allows you to specify the element that will have the `htmx-request` class
|
||||
|
@ -1,5 +1,9 @@
|
||||
+++
|
||||
title = "hx-inherit"
|
||||
description = """\
|
||||
The hx-inherit attribute in htmx allows you to explicitly control attribute inheritance behavior between parent and \
|
||||
child elements, providing fine-grained control over which htmx attributes are inherited when the default inheritance \
|
||||
system is disabled through configuration."""
|
||||
+++
|
||||
|
||||
The default behavior for htmx is to "inherit" many attributes automatically: that is, an attribute such as
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-on"
|
||||
description = """\
|
||||
The hx-on attributes in htmx allow you to write inline JavaScript event handlers directly on HTML elements, \
|
||||
supporting both standard DOM events and htmx-specific events with improved locality of behavior."""
|
||||
+++
|
||||
|
||||
The `hx-on*` attributes allow you to embed scripts inline to respond to events directly on an element; similar to the
|
||||
|
@ -1,5 +1,7 @@
|
||||
+++
|
||||
title = "hx-params"
|
||||
description = """\
|
||||
The hx-params attribute in htmx allows you to filter the parameters that will be submitted with an AJAX request."""
|
||||
+++
|
||||
|
||||
The `hx-params` attribute allows you to filter the parameters that will be submitted with an AJAX request.
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-patch"
|
||||
description = """\
|
||||
The hx-patch attribute in htmx will cause an element to issue a PATCH request to the specified URL and swap the \
|
||||
returned HTML into the DOM using a swap strategy."""
|
||||
+++
|
||||
|
||||
The `hx-patch` attribute will cause an element to issue a `PATCH` to the specified URL and swap
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-post"
|
||||
description = """\
|
||||
The hx-post attribute in htmx will cause an element to issue a POST request to the specified URL and swap the \
|
||||
returned HTML into the DOM using a swap strategy."""
|
||||
+++
|
||||
|
||||
The `hx-post` attribute will cause an element to issue a `POST` to the specified URL and swap
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-preserve"
|
||||
description = """\
|
||||
The hx-preserve attribute in htmx allows you to keep an element unchanged during HTML replacement. Elements with \
|
||||
hx-preserve set are preserved by `id` when htmx updates any ancestor element."""
|
||||
+++
|
||||
|
||||
The `hx-preserve` attribute allows you to keep an element unchanged during HTML replacement.
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-prompt"
|
||||
description = """\
|
||||
The hx-prompt attribute in htmx allows you to show a prompt before issuing a request. The value of the prompt will \
|
||||
be included in the request in the `HX-Prompt` header."""
|
||||
+++
|
||||
|
||||
The `hx-prompt` attribute allows you to show a prompt before issuing a request. The value of
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-push-url"
|
||||
description = """\
|
||||
The hx-push-url attribute in htmx allows you to push a URL into the browser location history. This creates a new \
|
||||
history entry, allowing navigation with the browser's back and forward buttons."""
|
||||
+++
|
||||
|
||||
The `hx-push-url` attribute allows you to push a URL into the browser [location history](https://developer.mozilla.org/en-US/docs/Web/API/History_API).
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-put"
|
||||
description = """\
|
||||
The hx-put attribute in htmx will cause an element to issue a PUT request to the specified URL and swap the returned \
|
||||
HTML into the DOM using a swap strategy."""
|
||||
+++
|
||||
|
||||
The `hx-put` attribute will cause an element to issue a `PUT` to the specified URL and swap
|
||||
|
@ -1,5 +1,7 @@
|
||||
+++
|
||||
title = "hx-replace-url"
|
||||
description = """\
|
||||
The hx-replace-url attribute in htmx allows you to replace the current URL of the browser location history."""
|
||||
+++
|
||||
|
||||
The `hx-replace-url` attribute allows you to replace the current url of the browser [location history](https://developer.mozilla.org/en-US/docs/Web/API/History_API).
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-request"
|
||||
description = """\
|
||||
The hx-request attribute in htmx allows you to configure the request timeout, whether the request will send \
|
||||
credentials, and whether the request will include headers."""
|
||||
+++
|
||||
|
||||
The `hx-request` attribute allows you to configure various aspects of the request via the following attributes:
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-select-oob"
|
||||
description = """\
|
||||
The hx-select-oob attribute in htmx allows you to select content from a response to be swapped in via an out-of-band \
|
||||
swap. The value of this attribute is comma separated list of elements to be swapped out of band."""
|
||||
+++
|
||||
|
||||
The `hx-select-oob` attribute allows you to select content from a response to be swapped in via an out-of-band swap.
|
||||
@ -25,7 +28,7 @@ which will replace the entire button in the DOM, and, in addition, pick out an e
|
||||
in the response and swap it in for div in the DOM with the same ID.
|
||||
|
||||
Each value in the comma separated list of values can specify any valid [`hx-swap`](@/attributes/hx-swap.md)
|
||||
strategy by separating the selector and the swap strategy with a `:`.
|
||||
strategy by separating the selector and the swap strategy with a `:`, with the strategy otherwise defaulting to `outerHTML`.
|
||||
|
||||
For example, to prepend the alert content instead of replacing it:
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
+++
|
||||
title = "hx-select"
|
||||
description = "The hx-select attribute in htmx allows you to select the content you want swapped from a response."
|
||||
+++
|
||||
|
||||
The `hx-select` attribute allows you to select the content you want swapped from a response. The value of
|
||||
|
@ -1,9 +1,13 @@
|
||||
+++
|
||||
title = "hx-swap-oob"
|
||||
description = """\
|
||||
The hx-swap-oob attribute in htmx allows you to specify that some content in a response should be swapped into the \
|
||||
DOM somewhere other than the target, that is 'out-of-band'. This allows you to piggyback updates to other elements \
|
||||
on a response."""
|
||||
+++
|
||||
|
||||
The `hx-swap-oob` attribute allows you to specify that some content in a response should be
|
||||
swapped into the DOM somewhere other than the target, that is "Out of Band". This allows you to piggy back updates to other element updates on a response.
|
||||
swapped into the DOM somewhere other than the target, that is "Out of Band". This allows you to piggyback updates to other element updates on a response.
|
||||
|
||||
Consider the following response HTML:
|
||||
|
||||
@ -72,7 +76,7 @@ A `<p>` can be encapsulated in `<div>` or `<span>`:
|
||||
Note that you can use a `template` tag to encapsulate types of elements that, by the HTML spec, can't stand on their own in the
|
||||
DOM (`<tr>`, `<td>`, `<th>`, `<thead>`, `<tbody>`, `<tfoot>`, `<colgroup>`, `<caption>`, `<col>` & `<li>`).
|
||||
|
||||
Here is an example with an out of band swap of a table row being encapsulated in this way:
|
||||
Here is an example with an out-of-band swap of a table row being encapsulated in this way:
|
||||
|
||||
```html
|
||||
<div>
|
||||
@ -91,7 +95,7 @@ Note that these template tags will be removed from the final content of the page
|
||||
|
||||
Some element types, like SVG, use a specific XML namespace for their child elements. This prevents internal elements from working correctly when swapped in, unless they are encapsulated within a `svg` tag. To modify the internal contents of an existing SVG, you can use both `template` and `svg` tags to encapsulate the elements, allowing them to get the correct namespace applied.
|
||||
|
||||
Here is an example with an out of band swap of svg elements being encapsulated in this way:
|
||||
Here is an example with an out-of-band swap of svg elements being encapsulated in this way:
|
||||
|
||||
```html
|
||||
<div>
|
||||
@ -111,7 +115,7 @@ Note that these `template` and `svg` wrapping tags will be removed from the fina
|
||||
## Nested OOB Swaps
|
||||
|
||||
By default, any element with `hx-swap-oob=` attribute anywhere in the response is processed for oob swap behavior, including when an element is nested within the main response element.
|
||||
This can be problematic when using [template fragments](https://htmx.org/essays/template-fragments/) where a fragment may be reused as a oob-swap target and also as part of a bigger fragment. When the bigger fragment is the main response the inner fragment will still be processed as an oob swap, removing it from the dom.
|
||||
This can be problematic when using [template fragments](https://htmx.org/essays/template-fragments/) where a fragment may be reused as an oob-swap target and also as part of a bigger fragment. When the bigger fragment is the main response the inner fragment will still be processed as an oob swap, removing it from the dom.
|
||||
|
||||
This behavior can be changed by setting the config `htmx.config.allowNestedOobSwaps` to `false`. If this config option is `false`, OOB swaps are only processed when the element is *adjacent to* the main response element, OOB swaps elsewhere will be ignored and oob-swap-related attributes stripped.
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-swap"
|
||||
description = """\
|
||||
The hx-swap attribute in htmx allows you to specify the 'swap strategy', or how the response will be swapped in \
|
||||
relative to the target of an AJAX request. The default swap strategy is `innerHTML`."""
|
||||
+++
|
||||
|
||||
The `hx-swap` attribute allows you to specify how the response will be swapped in relative to the
|
||||
@ -54,7 +57,7 @@ Similarly, you can modify the time between the swap and the settle logic by incl
|
||||
modifier:
|
||||
|
||||
```html
|
||||
<!-- this will wait 1s before doing the swap after it is received -->
|
||||
<!-- this will wait 1s before doing the settle after it is received -->
|
||||
<div hx-get="/example" hx-swap="innerHTML settle:1s">Get Some HTML & Append It</div>
|
||||
```
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
+++
|
||||
title = "hx-sync"
|
||||
description = "The hx-sync attribute in htmx allows you to synchronize AJAX requests between multiple elements."
|
||||
+++
|
||||
|
||||
The `hx-sync` attribute allows you to synchronize AJAX requests between multiple elements.
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-target"
|
||||
description = """\
|
||||
The hx-target attribute in htmx allows you to target a different element for swapping than the one issuing the AJAX \
|
||||
request."""
|
||||
+++
|
||||
|
||||
The `hx-target` attribute allows you to target a different element for swapping than the one issuing the AJAX
|
||||
@ -16,7 +19,7 @@ request. The value of this attribute can be:
|
||||
(e.g. `next .error` will target the closest following sibling element with `error` class)
|
||||
* `previous` which resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling)
|
||||
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
|
||||
(e.g `previous .error` will target the closest previous sibling with `error` class)
|
||||
(e.g. `previous .error` will target the closest previous sibling with `error` class)
|
||||
|
||||
|
||||
Here is an example that targets a div:
|
||||
|
@ -1,5 +1,10 @@
|
||||
+++
|
||||
title = "hx-trigger"
|
||||
description = """\
|
||||
The hx-trigger attribute in htmx allows you to specify what triggers an AJAX request. Supported triggers include \
|
||||
standard DOM events, custom events, polling intervals, and event modifiers. The hx-trigger attribute also allows \
|
||||
specifying event filtering, timing controls, event bubbling, and multiple trigger definitions for fine-grained \
|
||||
control over when and how requests are initiated."""
|
||||
+++
|
||||
|
||||
The `hx-trigger` attribute allows you to specify what triggers an AJAX request. A trigger
|
||||
@ -11,6 +16,8 @@ value can be one of the following:
|
||||
|
||||
### Standard Events
|
||||
|
||||
Standard events refer to [web API events](https://developer.mozilla.org/en-US/docs/Web/API/Element#events) (e.g. click, keydown, mouseup, load).
|
||||
|
||||
A standard event, such as `click` can be specified as the trigger like so:
|
||||
|
||||
```html
|
||||
@ -49,7 +56,7 @@ for a global symbol with the name `foo`
|
||||
Standard events can also have modifiers that change how they behave. The modifiers are:
|
||||
|
||||
* `once` - the event will only trigger once (e.g. the first click)
|
||||
* `changed` - the event will only change if the value of the element has changed. Please pay attention `change` is the name of the event and `changed` is the name of the modifier.
|
||||
* `changed` - the event will only fire if the value of the element has changed. Please pay attention `change` is the name of the event and `changed` is the name of the modifier.
|
||||
* `delay:<timing declaration>` - a delay will occur before an event triggers a request. If the event
|
||||
is seen again it will reset the delay.
|
||||
* `throttle:<timing declaration>` - a throttle will occur after an event triggers a request. If the event
|
||||
@ -67,7 +74,7 @@ is seen again before the delay completes, it is ignored, the element will trigge
|
||||
(e.g. `next .error` will target the closest following sibling element with `error` class)
|
||||
* `previous` resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling)
|
||||
* `previous <CSS selector>` scans the DOM backwards for the first element that matches the given CSS selector.
|
||||
(e.g `previous .error` will target the closest previous sibling with `error` class)
|
||||
(e.g. `previous .error` will target the closest previous sibling with `error` class)
|
||||
* `target:<CSS selector>` - allows you to filter via a CSS selector on the target of the event. This can be useful when you want to listen for
|
||||
triggers from elements that might not be in the DOM at the point of initialization, by, for example, listening on the body,
|
||||
but with a target filter for a child element
|
||||
@ -79,12 +86,12 @@ but with a target filter for a child element
|
||||
* `all` - queue all events (issue a request for each event)
|
||||
* `none` - do not queue new events
|
||||
|
||||
Here is an example of a search box that searches on `keyup`, but only if the search value has changed
|
||||
Here is an example of a search box that searches on `input`, but only if the search value has changed
|
||||
and the user hasn't typed anything new for 1 second:
|
||||
|
||||
```html
|
||||
<input name="q"
|
||||
hx-get="/search" hx-trigger="keyup changed delay:1s"
|
||||
hx-get="/search" hx-trigger="input changed delay:1s"
|
||||
hx-target="#search-results"/>
|
||||
```
|
||||
|
||||
@ -155,3 +162,4 @@ The AJAX request can be triggered via JavaScript [`htmx.trigger()`](@/api.md#tri
|
||||
* `hx-trigger` is not inherited
|
||||
* `hx-trigger` can be used without an AJAX request, in which case it will only fire the `htmx:trigger` event
|
||||
* In order to pass a CSS selector that contains whitespace (e.g. `form input`) to the `from`- or `target`-modifier, surround the selector in parentheses or curly brackets (e.g. `from:(form input)` or `from:closest (form input)`)
|
||||
* A reset event in hx-trigger (e.g. hx-trigger="change, reset") might not work as intended, since HTMX builds its values and sends a request before the browser resets the form values. As a workaround, add a delay to let the browser reset the form before making the request (e.g. hx-trigger="change, reset delay:0.01s").
|
||||
|
@ -1,5 +1,8 @@
|
||||
+++
|
||||
title = "hx-validate"
|
||||
description = """\
|
||||
The hx-validate attribute in htmx will cause an element to validate itself using the HTML5 Validation API before it \
|
||||
submits a request."""
|
||||
+++
|
||||
|
||||
The `hx-validate` attribute will cause an element to validate itself by way of the [HTML5 Validation API](@/docs.md#validation)
|
||||
|
@ -1,5 +1,7 @@
|
||||
+++
|
||||
title = "hx-vals"
|
||||
description = """\
|
||||
The hx-vals attribute in htmx allows you to add to the parameters that will be submitted with an AJAX request."""
|
||||
+++
|
||||
|
||||
The `hx-vals` attribute allows you to add to the parameters that will be submitted with an AJAX request.
|
||||
|
@ -1,5 +1,9 @@
|
||||
+++
|
||||
title = "hx-vars"
|
||||
description = """\
|
||||
The hx-vars attribute in htmx allows you to dynamically add to the parameters that will be submitted with an AJAX \
|
||||
request. This attribute has been deprecated. We recommend you use the hx-vals attribute that provides the same \
|
||||
functionality with safer defaults."""
|
||||
+++
|
||||
|
||||
**NOTE: `hx-vars` has been deprecated in favor of [`hx-vals`](@/attributes/hx-vals.md), which is safer by default.**
|
||||
|
@ -101,10 +101,13 @@ It's worth mentioning that, if you prefer, you can use the [`data-`](https://htm
|
||||
<a data-hx-post="/click">Click Me!</a>
|
||||
```
|
||||
|
||||
Finally, [Version 1](https://v1.htmx.org) of htmx is still supported and supports IE11.
|
||||
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
|
||||
|
||||
[Version 1](https://v1.htmx.org) of htmx is still supported and supports IE11, but the latest version of htmx is 2.x.
|
||||
|
||||
If you are migrating to htmx 2.x from [htmx 1.x](https://v1.htmx.org), please see the [htmx 1.x migration guide](@/migration-guide-htmx-1.md).
|
||||
|
||||
If you are migrating to htmx from intercooler.js, please see the [intercooler migration guide](@/migration-guide-intercooler.md).
|
||||
@ -126,7 +129,7 @@ your head tag and get going:
|
||||
An unminified version is also available as well:
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/htmx.org@2.0.5/dist/htmx.js" integrity="sha384-oeUn82QNXPuVkGCkcrInrS1twIxKhkZiFfr2TdiuObZ3n3yIeMiqcRzkIcguaof1" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.js" integrity="sha384-oeUn82QNXPuVkGCkcrInrS1twIxKhkZiFfr2TdiuObZ3n3yIeMiqcRzkIcguaof1" crossorigin="anonymous"></script>
|
||||
```
|
||||
|
||||
While the CDN approach is extremely simple, you may want to consider
|
||||
@ -959,7 +962,7 @@ The fields available for response handling configuration on entries in this arra
|
||||
* `target` - A CSS selector specifying an alternative target for the response
|
||||
* `swapOverride` - An alternative swap mechanism for the response
|
||||
|
||||
#### Configuring Response Handling Examples {#response-handling}
|
||||
#### Configuring Response Handling Examples {#response-handling-examples}
|
||||
|
||||
As an example of how to use this configuration, consider a situation when a server-side framework responds with a
|
||||
[`422 - Unprocessable Entity`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422) response when validation errors occur. By default, htmx will ignore the response,
|
||||
@ -1117,30 +1120,11 @@ htmx provides an [extensions](/extensions) mechanism that allows you to customiz
|
||||
Extensions [are defined in javascript](/extensions/building) and then enabled via
|
||||
the [`hx-ext`](@/attributes/hx-ext.md) attribute.
|
||||
|
||||
Here is how you would install the [idiomorph](@extension/idiomorph) extension, which allows you to use the
|
||||
[Idiomorph](https://github.com/bigskysoftware/idiomorph) DOM morphing algorithms for htmx swaps:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<script src="https://unpkg.com/idiomorph@0.3.0/dist/idiomorph-ext.min.js"></script>
|
||||
</head>
|
||||
<body hx-ext="morph">
|
||||
...
|
||||
<button hx-post="/example" hx-swap="morph" hx-target="#content">
|
||||
Update Content
|
||||
</button>
|
||||
...
|
||||
</body>
|
||||
```
|
||||
|
||||
First the extension is included (it should be included *after* `htmx.js` is included), then the extension is referenced
|
||||
by name via the `hx-ext` attribute. This enables you to then use the `morph` swap.
|
||||
|
||||
### Core Extensions
|
||||
|
||||
htmx supports a few "core" extensions, which are supported by the htmx development team:
|
||||
|
||||
* [head-support](/extensions/head-support) - support for merging head tag information (styles, etc.) in htmx requests |
|
||||
* [head-support](/extensions/head-support) - support for merging head tag information (styles, etc.) in htmx requests
|
||||
* [htmx-1-compat](/extensions/htmx-1-compat) - restores htmx 1 defaults & functionality
|
||||
* [idiomorph](/extensions/idiomorph) - supports the `morph` swap strategy using idiomorph
|
||||
* [preload](/extensions/preload) - allows you to preload content for better performance
|
||||
@ -1150,6 +1134,56 @@ htmx supports a few "core" extensions, which are supported by the htmx developme
|
||||
|
||||
You can see all available extensions on the [Extensions](/extensions) page.
|
||||
|
||||
### Installing Extensions
|
||||
|
||||
The fastest way to install htmx extensions created by others is to load them via a CDN. Remember to always include the core htmx library before the extensions and [enable the extension](#enabling-extensions). For example, if you would like to use the [response-targets](/extensions/response-targets) extension, you can add this to your head tag:
|
||||
```HTML
|
||||
<head>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/htmx-ext-response-targets@2.0.2" integrity="sha384-T41oglUPvXLGBVyRdZsVRxNWnOOqCynaPubjUVjxhsjFTKrFJGEMm3/0KGmNQ+Pg" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body hx-ext="extension-name">
|
||||
...
|
||||
```
|
||||
An unminified version is also available at `https://unpkg.com/htmx-ext-extension-name/dist/extension-name.js` (replace `extension-name` with the name of the extension).
|
||||
|
||||
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install htmx extensions is to simply copy them into your project. Download the extension from `https://unpkg.com/htmx-ext-extension-name` (replace `extension-name` with the name of the extension) e.g., https://unpkg.com/htmx-ext-response-targets. Then add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
|
||||
|
||||
For npm-style build systems, you can install htmx extensions via [npm](https://www.npmjs.com/) (replace `extension-name` with the name of the extension):
|
||||
```shell
|
||||
npm install htmx-ext-extension-name
|
||||
```
|
||||
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-extension-name/dist/extension-name.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
|
||||
|
||||
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
|
||||
- Install `htmx.org` and `htmx-ext-extension-name` via npm (replace `extension-name` with the name of the extension)
|
||||
- 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
|
||||
```
|
||||
|
||||
Note: [Idiomorph](/extensions/idiomorph) does not follow the naming convention of htmx extensions. Use `idiomorph` instead of `htmx-ext-idiomorph`. For example, `https://unpkg.com/idiomorph` or `npm install idiomorph`.
|
||||
|
||||
Note: Community extensions hosted outside this repository might have different installation instructions. Please check the corresponding repository for set-up guidance.
|
||||
|
||||
### Enabling Extensions
|
||||
|
||||
To enable an extension, add a `hx-ext="extension-name"` attribute to `<body>` or another HTML element (replace `extension-name` with the name of the extension). The extension will be applied to all child elements.
|
||||
|
||||
The following example shows how to enable [response-targets](/extensions/response-targets) extension, allowing you to specify different target elements to be swapped based on HTTP response code.
|
||||
```html
|
||||
<body hx-ext="response-targets">
|
||||
...
|
||||
<button hx-post="/register" hx-target="#response-div" hx-target-404="#not-found">
|
||||
Register!
|
||||
</button>
|
||||
<div id="response-div"></div>
|
||||
<div id="not-found"></div>
|
||||
...
|
||||
</body>
|
||||
```
|
||||
|
||||
### Creating Extensions
|
||||
|
||||
If you are interested in adding your own extension to htmx, please [see the extension docs](/extensions/building).
|
||||
@ -1412,6 +1446,9 @@ be possible using a standard `on*` property, but it can be done using the `hx-on
|
||||
|
||||
Here the `example` parameter is added to the `POST` request before it is issued, with the value 'Hello Scripting!'.
|
||||
|
||||
Another usecase is to [reset user input](@/examples/reset-user-input.md) on successful requests using the `afterRequest`
|
||||
event, avoiding the need for something like an out of band swap.
|
||||
|
||||
The `hx-on*` attributes are a very simple mechanism for generalized embedded scripting. It is _not_ a replacement for more
|
||||
fully developed front-end scripting solutions such as AlpineJS or hyperscript. It can, however, augment a VanillaJS-based
|
||||
approach to scripting in your htmx-powered application.
|
||||
@ -1654,6 +1691,25 @@ with application security.
|
||||
A full discussion of CSPs is beyond the scope of this document, but the [MDN Article](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) provide a good jumping off point
|
||||
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.
|
||||
|
||||
```html
|
||||
<html lang="en" hx-headers='{"X-CSRF-TOKEN": "CSRF_TOKEN_INSERTED_HERE"}'>
|
||||
:
|
||||
</html>
|
||||
```
|
||||
|
||||
```html
|
||||
<body hx-headers='{"X-CSRF-TOKEN": "CSRF_TOKEN_INSERTED_HERE"}'>
|
||||
:
|
||||
</body>
|
||||
```
|
||||
|
||||
The above elements are usually unique in an HTML document and should be easy to locate within templates.
|
||||
|
||||
|
||||
## Configuring htmx {#config}
|
||||
|
||||
Htmx has some configuration options that can be accessed either programmatically or declaratively. They are
|
||||
|
@ -1,9 +1,13 @@
|
||||
+++
|
||||
title = "10 Tips For Building SSR/HDA applications"
|
||||
description = """\
|
||||
In this guide, Carson Gross provides ten practical tips to help developers transition from Single Page Applications \
|
||||
(SPAs) to Server-Side Rendering and Hypermedia-Driven Applications, focusing on essential mindset shifts and \
|
||||
architectural advantages."""
|
||||
date = 2022-06-13
|
||||
updated = 2023-06-13
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
+++
|
||||
title = "Essays"
|
||||
insert_anchor_links = "left"
|
||||
insert_anchor_links = "heading"
|
||||
sort_by = "date"
|
||||
generate_feeds = true
|
||||
template = "essay_index.html"
|
||||
page_template = "essay.html"
|
||||
+++
|
||||
|
||||
@ -8,15 +11,10 @@ page_template = "essay.html"
|
||||
* [HATEOAS](@/essays/hateoas.md)
|
||||
* [How Did REST Come To Mean The Opposite of REST?](@/essays/how-did-rest-come-to-mean-the-opposite-of-rest.md)
|
||||
* [Two Approaches To Decoupling](@/essays/two-approaches-to-decoupling.md)
|
||||
* [Hypermedia APIs vs. Data APIs](@/essays/hypermedia-apis-vs-data-apis.md)
|
||||
* [Splitting Your Data & Application APIs: Going Further](@/essays/splitting-your-apis.md)
|
||||
* [Hypermedia Clients](@/essays/hypermedia-clients.md)
|
||||
* [HATEOAS Is For Humans](https://intercoolerjs.org/2016/05/08/hatoeas-is-for-humans.html)
|
||||
* [Rescuing REST From the API Winter](https://intercoolerjs.org/2016/01/18/rescuing-rest.html)
|
||||
* [Taking HTML Seriously](https://intercoolerjs.org/2020/01/14/taking-html-seriously)
|
||||
* [REST Copypasta](@/essays/rest-copypasta.md)
|
||||
* [The #ViewSource Affordance](@/essays/right-click-view-source.md)
|
||||
* [Hypermedia Controls: Feral to Formal (ACM HT'24)](https://dl.acm.org/doi/pdf/10.1145/3648188.3675127)
|
||||
|
||||
### Why Hypermedia? Why Multi-Page Applications?
|
||||
* [Hypermedia On Whatever you'd Like (HOWL)](@/essays/hypermedia-on-whatever-youd-like.md)
|
||||
@ -24,11 +22,11 @@ page_template = "essay.html"
|
||||
* [When To Use Hypermedia?](@/essays/when-to-use-hypermedia.md)
|
||||
* [The API Churn/Security Trade-off](https://intercoolerjs.org/2016/02/17/api-churn-vs-security.html)
|
||||
* [Does Hypermedia Scale?](@/essays/does-hypermedia-scale.md)
|
||||
* [SPA Alternative](@/essays/spa-alternative.md)
|
||||
|
||||
### Real World htmx Experiences
|
||||
* [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)
|
||||
* [Next.js to htmx — A Real World Example](@/essays/a-real-world-nextjs-to-htmx-port.md)
|
||||
* [You Can't Build Interactive Web Apps Except as Single Page Applications... And Other Myths](@/essays/you-cant.md)
|
||||
|
||||
@ -36,6 +34,7 @@ page_template = "essay.html"
|
||||
* [htmx sucks](@/essays/htmx-sucks.md)
|
||||
* [Why Gumroad Didn't Choose htmx](@/essays/why-gumroad-didnt-choose-htmx.md)
|
||||
* [A Modest Critique of htmx](https://chrisdone.com/posts/htmx-critique/) [[Response]](https://news.ycombinator.com/item?id=41782080)
|
||||
* [Alternatives](@/essays/alternatives.md)
|
||||
|
||||
### Building Hypermedia Applications
|
||||
* [Hypermedia-Driven Applications (HDAs)](@/essays/hypermedia-driven-applications.md)
|
||||
@ -58,16 +57,25 @@ page_template = "essay.html"
|
||||
* [Why htmx Does Not Have a Build Step](@/essays/no-build-step.md)
|
||||
* [Is htmx Just Another JavaScript Framework?](@/essays/is-htmx-another-javascript-framework.md)
|
||||
* [htmx Implementation Deep Dive (Video)](https://www.youtube.com/watch?v=javGxN-h9VQ)
|
||||
* [Prefer If Statements to Polymorphism, etc.](@/essays/prefer-if-statements.md)
|
||||
* [Vendoring](@/essays/vendoring.md)
|
||||
|
||||
### Hypermedia History
|
||||
### Hypermedia Research
|
||||
|
||||
* [A File Structure For The Complex, The Changing and the Indeterminate (Ted Nelson, 1965)](https://dl.acm.org/doi/pdf/10.1145/800197.806036)
|
||||
* [The Mother Of All Demos (Doug Englebart, 1968)](https://www.youtube.com/watch?v=B6rKUf9DWRI)
|
||||
* [The First Web Page (1991)](http://info.cern.ch/hypertext/WWW/TheProject.html)
|
||||
* [Architectural Styles and the Design of Network-based Software Architectures (Roy Fielding, 2000)](https://ics.uci.edu/~fielding/pubs/dissertation/top.htm)
|
||||
* [State of the Art Review on Hypermedia Issues and Applications (2006)](https://paul.luon.net/hypermedia/index.html) [[archive]](https://web.archive.org/web/20240428215142/https://paul.luon.net/hypermedia/index.html)
|
||||
* [Interview: Henning Koch, Creator of Unpoly](@/essays/interviews/henning_koch.md)
|
||||
* [Hypermedia Controls: Feral to Formal (ACM HT'24)](https://dl.acm.org/doi/pdf/10.1145/3648188.3675127)
|
||||
* [Preserving REST-ful Visibility Of Rich Web Applications With Generalized Hypermedia Controls (ACM SIGWEB Newsletter, Autumn'24)](https://hypermedia.cs.montana.edu/papers/preserving-restful.pdf)
|
||||
|
||||
### Interviews
|
||||
|
||||
* [Henning Koch](@/essays/interviews/henning_koch.md), creator of [Unpoly](https://unpoly.com/)
|
||||
* [Makinde Adeagbo](@/essays/interviews/makinde_adeagbo.md), creator of [Primer](https://www.youtube.com/watch?v=wHlyLEPtL9o)
|
||||
* [Chris Wanstrath aka @defunkt](@/essays/interviews/chris_wanstrath.md), creator of [pjax](https://github.com/defunkt/jquery-pjax)
|
||||
* [Mike Amundsen](@/essays/interviews/mike_amundsen.md), author of [RESTful Web APIs](http://restfulwebapis.com/)
|
||||
* [Leonard Richardson](@/essays/interviews/leonard_richardson.md), creator of the [RMM](https://en.wikipedia.org/wiki/Richardson_Maturity_Model)
|
||||
|
||||
## Banners
|
||||
<div style="text-align: center;margin:32px">
|
||||
|
@ -1,9 +1,13 @@
|
||||
+++
|
||||
title = "Next.js to htmx — A Real World Example"
|
||||
description = """\
|
||||
In this case study, Pouria Ezzati examines how migrating a URL shortener from Next.js to htmx resulted in \
|
||||
significant improvements in codebase size, dependencies, and developer experience while challenging assumptions \
|
||||
about modern web frameworks."""
|
||||
date = 2024-11-07
|
||||
updated = 2024-11-07
|
||||
authors = ["Pouria Ezzati"]
|
||||
[taxonomies]
|
||||
author = ["Pouria Ezzati"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "A Real World React -> htmx Port"
|
||||
description = """\
|
||||
David Guillot at Contexte gave what we are calling 'The Mother of All htmx Demos' at DjangoCon 2022. This essay \
|
||||
summarizes this real-world case study of replacing React with htmx in a SaaS product, demonstrating significant \
|
||||
improvements in code size, performance, and development team efficiency through the adoption of a hypermedia-driven \
|
||||
architecture."""
|
||||
date = 2022-09-29
|
||||
updated = 2022-10-15
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
51
www/content/essays/a-real-world-wasm-to-htmx-port.md
Normal file
51
www/content/essays/a-real-world-wasm-to-htmx-port.md
Normal file
@ -0,0 +1,51 @@
|
||||
+++
|
||||
title = "A Real World wasm to htmx Port"
|
||||
description = """\
|
||||
In this article, Joe Fioti describes their journey of simplifying their web application architecture by moving from \
|
||||
a complex WebAssembly-based system to a streamlined htmx solution, resulting in significantly reduced code \
|
||||
complexity and improved development efficiency."""
|
||||
date = 2025-01-10
|
||||
updated = 2025-01-10
|
||||
authors = ["Joe Fioti"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
<style>
|
||||
img, video {
|
||||
max-width: 100%;
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
When I was in college, I wrote some customer service software that tied together some custom AI models I trained, the OpenAI API, a database, and some social media APIs to make the first version of [Sidekick](https://sidekickai.co).
|
||||
|
||||
## Led astray
|
||||
|
||||
Over the next couple years I worked on adding more features and growing the user base. As a solo founder, I should have been focused on sales, marketing, and market discovery. Instead, as an engineer, I wanted to hand-craft the perfect web stack. I was firmly of the belief that the network gap between the frontend and the backend could be abstracted away, and I could make writing web apps as simple as writing native apps. Did this have anything to do with my business, product, or customers? Absolutely not, but as many technical founders do, I believed if I perfected the tech, the customers would materialize.
|
||||
|
||||
My design decisions were naive, but also reminiscent to what's seen in industry today: I wanted the backend and frontend to share a language (Rust), I wanted compile-time checks across the network boundary, I wanted to write frontend code like it was an app (reactive), and I wanted nearly instant reload times. What I got out of it was a buggy mess.
|
||||
|
||||
I had invented a system where simple rust functions can be tagged with a macro to generate a backend route and a frontend request function, so you can call the function like it was a standard function, and it would run on the backend. A true poor-mans GraphQL. My desire to write Rust on the frontend required I compile a WASM bundle. My desire for instant load times required isomorphic SSR. All of this complexity, for what was essentially a simple CRUD site.
|
||||
|
||||
## A better way
|
||||
|
||||
At this point Sidekick has grown and it now has a codebase which is responsible for not-insignificant volumes of traffic each day. There was this point where I looked into HTMX, multi-page websites, and HATEOAS, and realized the Sidekick codebase, which had grown into ~36k lines spread over 8 different crates, could be folded into a single crate, a single binary that ran the backend, which generated the frontend on demand through templating, and that HTMX could suffice for all the interactivity we required.
|
||||
|
||||
Large refactors typically have a bad track record so we wrote a quick and dirty simplified version of part of the site to convince ourselves it could work. After sufficient convincing, we undertook a full rewrite. All said and done, the rewrite took approximately 3 weeks of intense work. The results were dramatic:
|
||||
|
||||
- **36k LOC -> 8k LOC**
|
||||
- **8 crates -> 1 crate**
|
||||
- **~5 bug reports / week -> ~1 bug report / week**
|
||||
- **More full nights of sleep**
|
||||
|
||||

|
||||
|
||||
|
||||
The rewrite went far better than I could have imagined. It definitely won't be representative of every experience, our app was definitely uniquely suited to HTMX. Axum and some custom middleware also went a long way for sharing common infrastructure across the site. Though we don't have proper metrics, we've anecdotally noticed significantly improved load times.
|
||||
|
||||
## Reflection
|
||||
|
||||
I'll finish by touching on the biggest benefit in my eyes: it's tremendously easier to add new features as our customers request them. A feature that would have taken 2 weeks to fully implement, test and ship, now takes a day or two. As a small startup with a large number of customer demands, this is table stakes.
|
||||
|
||||
Sidekick hasn't raised VC funding so I can't afford to hire lots of devs. With HTMX we don't need to.
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "A Response To "Have Single-Page Apps Ruined the Web?""
|
||||
description = """\
|
||||
In this essay, Carson Gross gives an analysis of Rich Harris's talk 'Have Single-Page Apps Ruined the Web?', \
|
||||
exploring the debate between Single-Page Applications (SPAs) and Multi-Page Applications (MPAs). Carson examines \
|
||||
Harris's criticisms of both approaches and proposes hypermedia-oriented solutions using htmx, while discussing the \
|
||||
broader implications for web development architecture and the future role of JavaScript in web applications."""
|
||||
date = 2021-12-24
|
||||
updated = 2022-05-27
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
5
www/content/essays/all/_index.md
Normal file
5
www/content/essays/all/_index.md
Normal file
@ -0,0 +1,5 @@
|
||||
+++
|
||||
title = "All Essays"
|
||||
insert_anchor_links = "left"
|
||||
template = "essay_all.html"
|
||||
+++
|
130
www/content/essays/alternatives.md
Normal file
130
www/content/essays/alternatives.md
Normal file
@ -0,0 +1,130 @@
|
||||
+++
|
||||
title = "Alternatives to htmx"
|
||||
description = """\
|
||||
In this article, Carson Gross compares several alternative libraries and frameworks to htmx that embrace a \
|
||||
hypermedia-oriented approach to web development. Carson explores established solutions like Unpoly and Hotwire \
|
||||
Turbo, as well as emerging projects like Triptych and htmz, providing developers with a comprehensive overview of \
|
||||
hypermedia-driven application development options beyond htmx."""
|
||||
date = 2025-01-12
|
||||
updated = 2024-01-12
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
[htmx](/) is only one of many different libraries & frameworks that take the
|
||||
[hypermedia oriented](@/essays/hypermedia-driven-applications.md) approach to building web applications. I have
|
||||
said before that I think the [ideas of htmx](/essays) / [hypermedia](https://hypermedia.systems) are more important than
|
||||
htmx as an implementation.
|
||||
|
||||
Here are some of my favorite other takes on these ideas that I think are worth your consideration:
|
||||
|
||||
## Unpoly
|
||||
|
||||
[Unpoly](https://unpoly.com/) is a wonderful, mature front end framework that has been used heavily (especially in the
|
||||
ruby community) for over a decade now. It offers best-in-class [progressive enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement)
|
||||
and has many useful concepts such as [layers](https://unpoly.com/up.layer) and sophisticated
|
||||
[form validation](https://unpoly.com/validation).
|
||||
|
||||
I interviewed the author, Henning Koch, [here](@/essays/interviews/henning_koch.md)
|
||||
|
||||
You can see a demo application using Unpoly [here](https://demo.unpoly.com/).
|
||||
|
||||
## Triptych
|
||||
|
||||
[Triptych](https://github.com/alexpetros/triptych) is a set of [three proposals](https://alexanderpetros.com/triptych/)
|
||||
to bring more generalized hypermedia controls directly into the HTML specification:
|
||||
|
||||
* Allow more [HTTP Methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) to be used directly from HTML
|
||||
* Allow buttons to act as stand-alone hypermedia controls
|
||||
* Allow hypermedia controls to target any element on the page for replacement
|
||||
|
||||
It is [in the process](https://github.com/whatwg/html/issues/3577#issuecomment-2294931398) of being introduced to the
|
||||
[WHATWG](https://whatwg.org/) for inclusion in the HTML specification.
|
||||
|
||||
The project includes a [polyfill](https://github.com/alexpetros/triptych/blob/main/triptych.js) that can be used today
|
||||
to implement applications using the proposal today.
|
||||
|
||||
## fixi.js
|
||||
|
||||
[fixi.js](https://github.com/bigskysoftware/fixi) is a minimalist implementation of
|
||||
[generalized hypermedia controls](https://dl.acm.org/doi/fullHtml/10.1145/3648188.3675127) by the htmx team, focusing
|
||||
on being as small as possible and [omitting](https://github.com/bigskysoftware/fixi#minimalism) many of the features
|
||||
found in htmx.
|
||||
|
||||
It is intended to be as small as possible (~3.5k unminified & uncompressed, ~1.3k compressed) while still being readable
|
||||
and debuggable, so it can be included in a project directly without requiring any transformations.
|
||||
|
||||
## Datastar
|
||||
|
||||
[Datastar](https://data-star.dev/) started life as a proposed rewrite of htmx in typescript and with modern
|
||||
tooling. It eventually became its own project and takes an [SSE-oriented](https://data-star.dev/guide/getting_started#backend-setup)
|
||||
approach to hypermedia.
|
||||
|
||||
Datastar combines functionality found in both htmx and [Alpine.js](https://alpinejs.dev/) into
|
||||
a single, tidy package that is smaller than htmx.
|
||||
|
||||
You can see many examples of Datastar in action [here](https://data-star.dev/examples).
|
||||
|
||||
## Alpine-ajax
|
||||
|
||||
Speaking of Alpine (which is a common library to use in conjunction with htmx) you should look at
|
||||
[Alpine AJAX](https://alpine-ajax.js.org/), an Alpine plugin which integrates htmx-like concepts directly into Alpine.
|
||||
|
||||
If you are already an Alpine enthusiast, Alpine AJAX allows you to stay in that world.
|
||||
|
||||
You can see many examples of Alpine AJAX in action [here](https://alpine-ajax.js.org/examples/).
|
||||
|
||||
## Hotwire Turbo
|
||||
|
||||
[Turbo](https://turbo.hotwired.dev/) is a component of the [Hotwire](https://hotwired.dev/) set of web development
|
||||
technologies by [37Signals](https://37signals.com/), of [Ruby on Rails](https://rubyonrails.org/) fame. It is a polished
|
||||
front end framework that is used heavily in the rails community, but can be used with other backend technologies as well.
|
||||
|
||||
Some people who have had a bad experience with htmx [have enjoyed turbo](https://news.ycombinator.com/item?id=42615663).
|
||||
|
||||
## htmz
|
||||
|
||||
[htmz](https://leanrada.com/htmz/) is a brilliant, tiny library that takes advantage of the fact that anchors and forms
|
||||
already have a [`target`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target) attribute that can target
|
||||
an `iframe`.
|
||||
|
||||
This, in combination with the `location hash`, is used to allow [generalized transclusion](https://dl.acm.org/doi/fullHtml/10.1145/3648188.3675127#sec-7).
|
||||
|
||||
This is the *entire* source of the library (I'm not joking):
|
||||
|
||||
```html
|
||||
<iframe hidden name=htmz onload="setTimeout(()=>document.querySelector(contentWindow.location.hash||null)?.replaceWith(...contentDocument.body.childNodes))"></iframe>
|
||||
```
|
||||
|
||||
Amazing!
|
||||
|
||||
## TwinSpark
|
||||
|
||||
[TwinSpark](https://twinspark.js.org/) is a library created by [Alexander Solovyov](https://solovyov.net/) that is
|
||||
similar to htmx, and includes features such as [morphing](https://twinspark.js.org/api/ts-swap/#morph).
|
||||
|
||||
It is being [used in production](https://twinspark.js.org#who-is-using-this) on sites with 100k+ daily users.
|
||||
|
||||
## jQuery
|
||||
|
||||
Finally, good ol' [jQuery](https://jquery.com/) has the the [`load()`](https://api.jquery.com/load/#load-url-data-complete)
|
||||
function that will load a given url into an element. This method was part of the inspiration for
|
||||
[intercooler.js](https://intercoolerjs.org), the precursor to htmx.
|
||||
|
||||
It is very simple to use:
|
||||
|
||||
```javascript
|
||||
$( "#result" ).load( "ajax/test.html" );
|
||||
```
|
||||
and might be enough for your needs if you are already using jQuery.
|
||||
|
||||
## Conclusion
|
||||
|
||||
I hope that if htmx isn't right for your application, one of these other libraries might be useful in allowing you to
|
||||
utilize the hypermedia model. There is a lot of exciting stuff happening in the hypermedia world right now, and these
|
||||
libraries each contribute to that.
|
||||
|
||||
Finally, if you have a moment, please give them (especially the newer ones) a star on Github: as an open source
|
||||
developer I know that Github stars are one of the best psychological boosts that help keep me going.
|
||||
|
@ -1,9 +1,13 @@
|
||||
+++
|
||||
title = "Another Real World React -> htmx Port"
|
||||
description = """\
|
||||
In this article, Carson Gross summarizes a real-world case study of Adrian McPhee porting the OpenUnited platform \
|
||||
from React to htmx, documenting significant reductions in code complexity and development time while highlighting \
|
||||
how content-focused web applications can benefit from a hypermedia architectural approach."""
|
||||
date = 2023-09-20
|
||||
updated = 2023-09-20
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,19 +1,25 @@
|
||||
+++
|
||||
title = "Architectural Sympathy"
|
||||
description = """\
|
||||
In this essay, Carson Gross explores architectural sympathy as a software design principle where new software adopts \
|
||||
and conforms to the architectural patterns of existing systems. Carson examines the application of architectural \
|
||||
sympathy in web development through htmx, its advantages and tradeoffs, and drawing parallels with medieval \
|
||||
cathedral construction to illustrate the balance between innovation and coherent design."""
|
||||
date = 2023-04-06
|
||||
updated = 2023-04-06
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
||||
# Mechanical Sympathy & Architectural Sympathy
|
||||
|
||||
> You don’t have to be an engineer to be be a racing driver, but you do have to have Mechanical Sympathy.
|
||||
> You don’t have to be an engineer to be a racing driver, but you do have to have Mechanical Sympathy.
|
||||
|
||||
_-Jackie Stewart, racing driver_
|
||||
|
||||
The term "mechanical sympathy" was originally coined by Jackie Steward to capture a characteristic
|
||||
The term "mechanical sympathy" was originally coined by Jackie Stewart to capture a characteristic
|
||||
of race car drivers, who needed a deep and intuitive understanding of how a race car worked in order
|
||||
to get the best possible performance out of the vehicle.
|
||||
|
||||
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "Codin' Dirty"
|
||||
description = """\
|
||||
In this article, Carson Gross discusses an alternative approach to software development that challenges the \
|
||||
principles outlined in 'Clean Code.' Carson advocates for allowing larger functions in certain cases, preferring \
|
||||
integration tests over unit tests, and minimizing the number of classes and interfaces. He shares examples from \
|
||||
successful software projects that demonstrate these practices can lead to maintainable, high-quality code."""
|
||||
date = 2024-11-24
|
||||
updated = 2024-11-24
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,9 +1,13 @@
|
||||
+++
|
||||
title = "Complexity Budget"
|
||||
description = """\
|
||||
In this essay, Carson Gross explores the concept of a complexity budget in software development. He discusses how \
|
||||
managing complexity across applications is a critical responsibility for architects and developers, while examining \
|
||||
strategies for effective complexity management and the challenges that arise when attempting to reduce it."""
|
||||
date = 2020-10-29
|
||||
updated = 2024-01-21
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,9 +1,15 @@
|
||||
+++
|
||||
title = "Does Hypermedia Scale?"
|
||||
description = """\
|
||||
In this essay, Carson Gross examines whether Hypermedia-Driven Applications (HDAs) can effectively scale across \
|
||||
different dimensions of software development, including system nodes, application performance, feature count, \
|
||||
feature complexity, and team size. The analysis explores both the strengths and limitations of hypermedia in each \
|
||||
context, ultimately demonstrating that HDAs can scale well in most scenarios while acknowledging specific challenges \
|
||||
with complex client-side features."""
|
||||
date = 2023-11-06
|
||||
updated = 2023-11-06
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -20,7 +26,7 @@ First of all, let's define the term "scaling" and then the contexts that word ca
|
||||
context, scaling typically means the ability of the software to handle "larger" things. Those things can be:
|
||||
|
||||
* More nodes in a general [system](https://hypermedia.systems)
|
||||
* More user requests (scaling your individual applications performance)
|
||||
* More user requests (scaling your individual application's performance)
|
||||
* More features (scaling your codebase)
|
||||
* More _complex_ features
|
||||
* More developers (scaling your team size)
|
||||
|
148
www/content/essays/future.md
Normal file
148
www/content/essays/future.md
Normal file
@ -0,0 +1,148 @@
|
||||
+++
|
||||
title = "The future of htmx"
|
||||
description = """\
|
||||
In this essay, Carson Gross and Alex Petros discuss htmx's future direction and philosophy. They explain how the \
|
||||
project aims to emulate jQuery's success through API stability, minimal feature additions, and quarterly releases \
|
||||
while continuing to promote hypermedia-driven development and support the broader web development ecosystem."""
|
||||
date = 2025-01-01
|
||||
updated = 2025-01-01
|
||||
authors = ["Carson Gross", "Alex Petros"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
## In The Beginning...
|
||||
|
||||
htmx began life as [intercooler.js](https://intercoolerjs.org), a library built around jQuery that added behavior based
|
||||
on HTML attributes.
|
||||
|
||||
For developers who are not familiar with it, [jQuery](https://jquery.com/) is a venerable JavaScript
|
||||
library that made writing cross-platform JavaScript a lot easier during a time when browser implementations were very
|
||||
inconsistent, and JavaScript didn’t have many of the convenient APIs and features that it does now.
|
||||
|
||||
Today many web developers consider jQuery to be “legacy software.” With all due respect to this perspective, jQuery is
|
||||
currently used on [75% of all public websites](https://w3techs.com/technologies/overview/javascript_library), a number that dwarfs all other JavaScript tools.
|
||||
|
||||
Why has jQuery remained so ubiquitous?
|
||||
|
||||
Here are three technical reasons we believe contribute to its ongoing success:
|
||||
|
||||
* It is very easy to add to a project (just a single, dependency-free link)
|
||||
* It has maintained a very consistent API, remaining largely backwards compatible over its life (intercooler.js works
|
||||
with jQuery v1, v2 and v3)
|
||||
* As a library, you can use as much or as little of it as you like: it stays out of the way otherwise and doesn’t
|
||||
dictate the structure of your application
|
||||
|
||||
## htmx is the New jQuery
|
||||
|
||||
Now, that’s a ridiculous (and arrogant) statement to make, of course, but it is an *ideal* that we on the htmx team are
|
||||
striving for.
|
||||
|
||||
In particular, we want to emulate these technical characteristics of jQuery that make it such a low-cost, high-value
|
||||
addition to the toolkits of web developers. Alex has
|
||||
discussed [“Building The 100 Year Web Service”](https://www.youtube.com/watch?v=lASLZ9TgXyc) and we want htmx to be a
|
||||
useful tool for exactly that use case.
|
||||
|
||||
Websites that are built with jQuery stay online for a very long time, and websites built with htmx should be capable of
|
||||
the same (or better).
|
||||
|
||||
Going forward, htmx will be developed with its *existing* users in mind.
|
||||
|
||||
If you are an existing user of htmx—or are thinking about becoming one—here’s what that means.
|
||||
|
||||
### Stability as a Feature
|
||||
|
||||
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.
|
||||
|
||||
Someone upgrading htmx (even from 1.x to 2.x) should expect things to continue working as before.
|
||||
|
||||
Where appropriate, we may add better configuration options, but we won’t change defaults.
|
||||
|
||||
### No New Features as a Feature
|
||||
|
||||
We are going to be increasingly inclined to not accept new proposed features in the library core.
|
||||
|
||||
People shouldn’t feel pressure to upgrade htmx over time unless there are specific bugs that they want fixed, and they
|
||||
should feel comfortable that the htmx that they write in 2025 will look very similar to htmx they write in 2035 and
|
||||
beyond.
|
||||
|
||||
We will consider new core features when new browser features become available, for example we
|
||||
are [already using](https://htmx.org/examples/move-before/) the experimental `moveBefore()` API on supported browsers.
|
||||
|
||||
However, we expect most new functionality to be explored and delivered via the
|
||||
htmx [extensions API](https://htmx.org/extensions/), and will work to make the extensions API more capable where
|
||||
appropriate.
|
||||
|
||||
### Quarterly Releases
|
||||
|
||||
Our release schedule is going to be roughly quarterly going forward.
|
||||
|
||||
There will be no death march upgrades associated with htmx, and there is no reason to monitor htmx releases for major
|
||||
functionality changes, just like with jQuery. If htmx 1.x is working fine for you, there is no reason to feel like you
|
||||
need to move to 2.x.
|
||||
|
||||
## Promoting Hypermedia
|
||||
|
||||
htmx does not aim to be a total solution for building web applications and services:
|
||||
it [generalizes hypermedia controls](https://dl.acm.org/doi/pdf/10.1145/3648188.3675127), and that’s roughly about it.
|
||||
|
||||
This means that a very important way to improve htmx — and one with lots of work remaining — is by helping improve the tools
|
||||
and techniques that people use *in conjunction* with htmx.
|
||||
|
||||
Doing so makes htmx dramatically more useful _without_ any changes to htmx itself.
|
||||
|
||||
### Supporting Supplemental Tools
|
||||
|
||||
While htmx gives you a few new tools in your HTML, it has no opinions about other important aspects of building your
|
||||
websites. A flagship feature of htmx is that it does not dictate what backend or database you use.
|
||||
|
||||
htmx is [compatible with lots of backends](https://htmx.org/essays/hypermedia-on-whatever-youd-like/), and we want to
|
||||
help make hypermedia-driven development work better for all of them.
|
||||
|
||||
One part of the hypermedia ecosystem that htmx has already helped improve is template engines. When
|
||||
we [first wrote](https://htmx.org/essays/template-fragments/) about how “template fragments” make defining partial page
|
||||
replacements much simpler, they were a relatively rare feature in template engines.
|
||||
|
||||
Not only are fragments much more common now, that essay
|
||||
is [frequently](https://github.com/mitsuhiko/minijinja/issues/260) [cited](https://github.com/sponsfreixes/jinja2-fragments)
|
||||
as an inspiration for building the feature.
|
||||
|
||||
There are many other ways that the experience of writing hypermedia-based applications can be improved, and we will
|
||||
remain dedicated to identifying and promoting those efforts.
|
||||
|
||||
### Writing, Research, and Standardization
|
||||
|
||||
Although htmx will not be changing dramatically going forward, we will continue energetically evangelizing the ideas of
|
||||
hypermedia.
|
||||
|
||||
In particular, we are trying to push [the ideas](https://dl.acm.org/doi/pdf/10.1145/3648188.3675127) of htmx into the
|
||||
HTML standard itself, via the [Triptych project](https://alexanderpetros.com/triptych/). In an ideal world, htmx
|
||||
functionality disappears into the web platform itself.
|
||||
|
||||
htmx code written *today* will continue working forever, of course, but in the very long run perhaps there will be no
|
||||
need to include the library to achieve [similar UI patterns](https://htmx.org/examples) via hypermedia.
|
||||
|
||||
## Intercooler Was Right
|
||||
|
||||
At the [end of the intercooler docs](https://intercoolerjs.org/docs#philosophy), we said this:
|
||||
|
||||
> Many javascript projects are updated at a dizzying pace. Intercooler is not.
|
||||
>
|
||||
> This is not because it is dead, but rather because it is (mostly) right: the basic idea is right, and the implementation
|
||||
at least right enough.
|
||||
>
|
||||
> This means there will not be constant activity and churn on the project, but rather
|
||||
a [stewardship](https://en.wikipedia.org/wiki/Stewardship_\(theology\)) relationship: the main goal now is to not screw
|
||||
it up. The documentation will be improved, tests will be added, small new declarative features will be added around the
|
||||
edges, but there will be no massive rewrite or constant updating. This is in contrast with the software industry in
|
||||
general and the front end world in particular, which has comical levels of churn.
|
||||
>
|
||||
> Intercooler is a sturdy, reliable tool for web development.
|
||||
|
||||
Leaving aside [the snark at the end of the third paragraph](https://www.youtube.com/watch?v=zGyAWH5btwY), this thinking
|
||||
is very much applicable to htmx. In fact, perhaps even more so since htmx is a standalone piece of software, benefiting
|
||||
from the experiences (and mistakes) of intercooler.js.
|
||||
|
||||
We hope to see htmx, in its own small way, join the likes of giants like jQuery as a sturdy and reliable tool for
|
||||
building your 100 year web services.
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "HATEOAS"
|
||||
description = """\
|
||||
In this essay, Carson Gross explores HATEOAS (Hypermedia as the Engine of Application State), explaining how it \
|
||||
enables REST APIs through hypermedia responses and contrasting it with modern JSON-based APIs. Using clear HTML \
|
||||
examples, Carson demonstrates how HATEOAS allows clients to discover available actions dynamically through \
|
||||
hypermedia rather than requiring prior knowledge of the API interface."""
|
||||
date = 2021-10-16
|
||||
updated = 2022-02-06
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
[extra]
|
||||
show_title = false
|
||||
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "How Did REST Come To Mean The Opposite of REST?"
|
||||
description = """\
|
||||
In this article, Carson Gross explores how the term REST (Representational State Transfer) evolved to mean nearly \
|
||||
the opposite of its original definition in modern web development. It traces how REST, originally defined by Roy \
|
||||
Fielding to describe the web's architecture of hypermedia-driven interactions, came to be widely misused as a term \
|
||||
for JSON-based APIs that lack the key hypermedia constraints that define true REST architectural style."""
|
||||
date = 2022-07-18
|
||||
updated = 2022-11-26
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -49,7 +54,7 @@ Only a few obstinate folks grumble: but these JSON APIs aren't RESTful!
|
||||
|
||||
In this post, I'd like to give you a [brief, incomplete and mostly wrong](https://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html)
|
||||
history of REST, and how we got to a place where its meaning has been nearly perfectly inverted to mean what REST was
|
||||
original contrasted with: RPC.
|
||||
originally contrasted with: RPC.
|
||||
|
||||
## Where Did REST Come From?
|
||||
|
||||
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "htmx sucks"
|
||||
description = """\
|
||||
This article provides a critical analysis of htmx, a web development library, explaining why the author believes it \
|
||||
represents a problematic approach to modern web development due to its outdated coding practices, lack of build \
|
||||
tools, absence of TypeScript support, and reliance on HTML-based architecture, while also questioning the \
|
||||
professionalism of its creator."""
|
||||
date = 2024-02-01
|
||||
updated = 2024-04-01
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "Hypermedia APIs vs. Data APIs"
|
||||
description = """\
|
||||
In this essay, Carson Gross explores the fundamental differences between hypermedia APIs and data APIs, He explains \
|
||||
how hypermedia APIs, which return HTML over HTTP, can embrace frequent changes due to their self-describing nature, \
|
||||
while data APIs require more stability and versioning to avoid breaking client code. Carson argues that these \
|
||||
distinct characteristics should inform different design approaches for each type of API."""
|
||||
date = 2021-07-17
|
||||
updated = 2022-04-07
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "Hypermedia Clients"
|
||||
description = """\
|
||||
In this essay, Carson Gross explores the critical but often overlooked role of hypermedia clients in REST \
|
||||
architectures. He explains why adding hypermedia controls to JSON APIs is insufficient for true RESTful systems \
|
||||
without proper client implementation. Carson examines the challenges of building hypermedia clients and argues that \
|
||||
web browsers remain the most practical choice for hypermedia-driven applications."""
|
||||
date = 2023-01-28
|
||||
updated = 2023-01-29
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,9 +1,13 @@
|
||||
+++
|
||||
title = "Hypermedia-Driven Applications"
|
||||
description = """\
|
||||
In this essay, Carson Gross explains the Hypermedia-Driven Application (HDA) architecture, which combines the \
|
||||
simplicity of traditional Multi-Page Applications with the enhanced user experience of Single-Page Applications by \
|
||||
extending HTML infrastructure through declarative syntax and hypermedia-based server interactions."""
|
||||
date = 2022-02-06
|
||||
updated = 2022-10-18
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,9 +1,15 @@
|
||||
+++
|
||||
title = "Hypermedia-Friendly Scripting"
|
||||
description = """\
|
||||
In this essay, Carson Gross discusses hypermedia-friendly scripting approaches for web applications, explaining how \
|
||||
to incorporate JavaScript while maintaining REST architectural principles and HATEOAS (Hypermedia as the Engine of \
|
||||
Application State). Carson outlines key guidelines for using scripting to enhance hypermedia-driven applications \
|
||||
without undermining their fundamental REST-ful architecture, covering topics like client-side state management, \
|
||||
event communication, and the islands architecture pattern."""
|
||||
date = 2022-11-17
|
||||
updated = 2022-11-29
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,8 +1,13 @@
|
||||
+++
|
||||
title = "Hypermedia On Whatever you'd Like"
|
||||
description = """\
|
||||
In this essay, Carson Gross explores the concept of 'The HOWL Stack' (Hypermedia On Whatever you'd Like) and argues \
|
||||
that using a hypermedia-driven approach for web applications allows developers to choose their preferred server-side \
|
||||
technology, freeing them from the pressure to use JavaScript throughout their entire stack while maintaining modern \
|
||||
web functionality through HTML and hypermedia enhancements."""
|
||||
date = 2023-05-23
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
3
www/content/essays/interviews/_index.md
Normal file
3
www/content/essays/interviews/_index.md
Normal file
@ -0,0 +1,3 @@
|
||||
+++
|
||||
render = false
|
||||
+++
|
166
www/content/essays/interviews/chris_wanstrath.md
Normal file
166
www/content/essays/interviews/chris_wanstrath.md
Normal file
@ -0,0 +1,166 @@
|
||||
+++
|
||||
title = "An interview with Chris Wanstrath aka @defunkt, Creator of pjax"
|
||||
description = """\
|
||||
This article features an in-depth interview with Chris Wanstrath (defunkt), the co-founder of GitHub and creator of \
|
||||
pjax, where he discusses his journey from early web development to creating pjax, an innovative JavaScript library \
|
||||
that helped bridge the gap between traditional web navigation and dynamic content loading."""
|
||||
date = 2025-01-27
|
||||
updated = 2025-01-27
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
I'm very excited to be able to interview @defunkt, the author of [pjax](https://github.com/defunkt/jquery-pjax), an
|
||||
early hypermedia-oriented javascript library that served as an inspiration for intercooler.js, which later became
|
||||
htmx. He's done a few other things too, like co-founding GitHub, but in this interview I want to focus on pjax, how it
|
||||
came to be, what influenced it and what it in turn influenced.
|
||||
|
||||
Thank you for agreeing to an interview @defunkt!
|
||||
|
||||
Q: To begin with, why don't you give the readers a bit of your background both professionally & technically:
|
||||
|
||||
> I think I can sum up most of my technical background in two quick anecdotes:
|
||||
>
|
||||
> 1. For "show and tell" in 6th grade, I brought in a printout of a web page I had made - including its source code. I
|
||||
> like to imagine that everyone was impressed.
|
||||
>
|
||||
> 2. Right after 7th grade, a bunch of rowdy high schoolers took me down to the local university during a Linux
|
||||
> installfest and put Red Hat on my family's old PC. That became my main computer for all of high school.
|
||||
>
|
||||
> So pretty much from the start I was a web-slinging, UNIX-loving hippie.
|
||||
>
|
||||
> In terms of coding, I started on QBasic using the IBM PC running OS/2 in my grandparents' basement. Then I got deep into
|
||||
> MUDs (and MUSHes and MUXes and MOOs...) which were written in C and usually included their own custom scripting
|
||||
> language. Writing C was "hardcoding", writing scripts was "softcoding". I had no idea what I was doing in C, but I
|
||||
> really liked the softcoding aspect.
|
||||
>
|
||||
> The same rowdy high schoolers who introduced me to Linux gave me the O'Reilly camel book and told me to learn Perl. I
|
||||
> did not enjoy it. But they also showed me php3, and suddenly it all came together: HTML combined with MUD-like
|
||||
> softcoding. I was hooked.
|
||||
>
|
||||
> I tried other things like ASP 3.0 and Visual Basic, but ultimately PHP was my jam for all of high school. I loved making
|
||||
> dynamic webpages, and I loved Linux servers. My friends and I had a comedy website in high school that shall remain
|
||||
> nameless, and I wrote the whole mysql/php backend myself before blogging software was popular. It was so much fun.
|
||||
>
|
||||
> My first year of college I switched to Gentoo and became fascinated with their package manager, which was written in
|
||||
> Python. You could write real Linux tools with it, which was amazing, but at the time the web story felt weak.
|
||||
>
|
||||
> I bought the huge Python O'Reilly book and was making my way through it when, randomly, I discovered Ruby on Rails. It
|
||||
> hit me like a bolt of lightning and suddenly my PHP and Python days were over.
|
||||
>
|
||||
> At the same time, Web 2.0 had just been coined and JavaScript was, like, "Hey, everyone. I've been here all along." So
|
||||
> as I was learning Rails, I was also learning JavaScript. Rails had helpers to abstract the JS away, but I actually
|
||||
> really liked the language (mostly) and wanted to learn it without relying on a framework or library.
|
||||
>
|
||||
> The combination of administering my own Linux servers, writing backend code in Rails, and writing frontend code in
|
||||
> JavaScript made me fall deeper in love with the web as a platform and exposed me to concepts like REST and HATEOAS.
|
||||
> Which, as someone who had been writing HTML for over a decade, felt natural and made sense.
|
||||
>
|
||||
> GitHub launched in 2008 powered by, surprise, Gentoo, Rails, and JavaScript. But due to GitHub's position as not just a
|
||||
> Rails community, but a collection of programming communities, I quickly evolved into a massive polyglot.
|
||||
>
|
||||
> I went back and learned Python, competing in a few programming competitions like Django Dash and attending (and
|
||||
> speaking) at different PyCons. I learned Objective-C and made Mac (and later iPhone) apps. I learned Scheme and Lisp,
|
||||
> eventually switching to Emacs from Vim and writing tons of Emacs Lisp. I went back and learned what all the sigils mean
|
||||
> in Perl. Then Lua, Java, C++, C, even C# - I wanted to try everything.
|
||||
>
|
||||
> And I'm still that way today. I've written projects in Go, Rust, Haskell, OCaml, F#, all sorts of Lisps (Chicken Scheme,
|
||||
> Clojure, Racket, Gambit), and more. I've written a dozen programming languages, including a few that can actually do
|
||||
> something. Right now I'm learning Zig.
|
||||
>
|
||||
> But I always go back to the web. It's why I created the Atom text editor using web technologies, it's why Electron
|
||||
> exists, and it's why I just cofounded the Ladybird Browser Initiative with Andreas Kling to develop the independent,
|
||||
> open source Ladybird web browser.
|
||||
|
||||
Q: Can you give me the history of how pjax came to be?
|
||||
|
||||
> It all starts with XMLHttpRequest, of course. Ajax. When I was growing up, walking to school both ways uphill in the
|
||||
> snow, the web was simple: you clicked on a link and a new web page loaded. Nothing fancy. It was a thing of beauty, and
|
||||
> it was good.
|
||||
>
|
||||
> Then folks started building email clients and all sorts of application-like programs in HTML using `<frames>` and
|
||||
> friends. It was not very beautiful, and not very good, but there was something there.
|
||||
>
|
||||
> Luckily, in the mid-2000s, Gmail and Ajax changed things. Hotmail had been around for a while, but Gmail was fast. By
|
||||
> updating content without a full page load using XMLHttpRequest, you could make a webpage that felt like a desktop
|
||||
> application without resorting to frames or other chicanery. And while other sites had used Ajax before Gmail, Gmail
|
||||
> became so popular that it really put this technique on the map.
|
||||
>
|
||||
> Soon Ajax, along with the ability to add rounded corners to web pages, ushered in the era known as Web 2.0. By 2010,
|
||||
> more and more web developers were pushing more and more of their code into JavaScript and loading dynamic content with
|
||||
> Ajax. There was just one problem: in the original, good model of the web, each page had a unique URL that you could use
|
||||
> to load its content in any context. This is one of the innovations of the web. When using Ajax, however, the URL doesn't
|
||||
> change. And even worse, it can't be changed - not the part that gets read by the server, anyway. The web was broken.
|
||||
>
|
||||
> As is tradition, developers created hacks to work around this limitation. The era of the #! began, pioneered by
|
||||
> Ajax-heavy sites like Facebook and Twitter. Instead of http://twitter.com/htmx_org, you'd
|
||||
> see http://twitter.com/#!/htmx_org in your browser's URL bar when visiting someone's profile. The # was traditionally
|
||||
> used for anchor tags, to link to a sub-section within a full web page, and could be modified by JavaScript. These
|
||||
> ancient web 2.0 developers took advantage of #'s malleability and started using it to represent permanent content that
|
||||
> could be updated inline, much like a real URL. The only problem was that your server code never saw the # part of a URL
|
||||
> when serving a request, so now you needed to start changing your backend architecture to make everything work.
|
||||
>
|
||||
> Oh, and it was all very buggy. That was a problem too.
|
||||
>
|
||||
> As an HTTP purist, I detested the #!. But I didn't have a better way.
|
||||
>
|
||||
> Time passed and lo, a solution appeared. One magical day, the #!s quietly disappeared from Facebook, replaced by good
|
||||
> old fashioned URLs. Had they abandoned Web 2.0? No... they had found a better way.
|
||||
>
|
||||
> The `history.pushState()` function, along with its sibling `history.replaceState()`, had been recently added to all
|
||||
> major web browsers. Facebook quickly took advantage of this new API to update the full URL in your browser whenever
|
||||
> changing content via Ajax, returning the web to its previous glory.
|
||||
>
|
||||
> And so there it was: the Missing Link.
|
||||
>
|
||||
> We had our solution, but now a new problem: GitHub was not an SPA, and I didn't want it to be one. By 2011 I had been
|
||||
> writing JavaScript for six years - more than enough time to know that too much JS is a terrible thing. The original
|
||||
> GitHub Issue Tracker was a Gmail-style web application built entirely in JS, circa 2009. It was an awful experience for
|
||||
> me, GitHub developers, and, ultimately, our users.
|
||||
>
|
||||
> That said, I still believed Ajax could dramatically speed up a web page's user interface and improve the overall
|
||||
> experience. I just didn't want to do it by writing lots of, or any, JavaScript. I liked the simple request/response
|
||||
> paradigm that the web was built on.
|
||||
>
|
||||
> Thus, Pjax was born. It sped up GitHub's UI by loading new pages via Ajax instead of full page loads, correctly updating
|
||||
> URLs while not requiring any JS beyond the Pjax library itself. Our developers could just tag a link with `[data-pjax]`
|
||||
> and our backend application would then automatically render a page's content without any layout, quickly getting you
|
||||
> just the data you need without asking the browser to reload any JS or CSS or HTML that didn't need to change. It also (
|
||||
> mostly) worked with the back button, just like regular web pages, and it had a JS API if you did need to dip into the
|
||||
> dark side and write something custom.
|
||||
>
|
||||
> The first commit to Pjax was Feb 26, 2011 and it was released publicly in late March 2011, after we had been using it to
|
||||
> power GitHub.com for some time.
|
||||
|
||||
Q: I recall it being a big deal in the rails community. Did the advent of turbolinks hurt adoption there?
|
||||
|
||||
> My goal wasn't really adoption of the library. If it was, I probably would have put in the work to decouple it from
|
||||
> jQuery. At the time, I was deep in building GitHub and wasn't the best steward of my many existing open source projects.
|
||||
>
|
||||
> What I wanted instead was adoption of the idea - I wanted people to know about `pushState()`, and I wanted people to
|
||||
> know there were ways to build websites other than just doing everything by hand in JavaScript. Rendering pages in whole
|
||||
> or in part on the server was still viable, and could be sped up using modern techniques.
|
||||
>
|
||||
> Turbolinks being created and integrated into Rails was amazing to see, and not entirely unsurprising. I was a huge fan
|
||||
> of Sam Stephenson's work even pre-GitHub, and we had very similiar ideas about HTTP and the web. Part of my thinking was
|
||||
> influenced by him and the Rails community, and part of what drew me to the Rails community was the shared ideas around
|
||||
> what's great about the web.
|
||||
>
|
||||
> Besides being coupled to jQuery, pjax's approach was quite limited. It was a simple library. I knew that other people
|
||||
> could take it further, and I'm glad they did.
|
||||
|
||||
Q: How much "theory" was there to pjax? Did you think much about hypermedia, REST, etc. when you were building it? (
|
||||
I backed into the theory after I had built intercooler, curious how it went for you!)
|
||||
|
||||
> Not much. It started by appending `?pjax=1` to every request, but before release we switched it to send an `X-PJAX`
|
||||
> header instead. Very fancy.
|
||||
>
|
||||
> Early GitHub developer Rick Olson (@technoweenie), also from the Rails community, was the person who introduced me to
|
||||
> HATEOAS and drove that philosophy in GitHub's API. So anything good about Pjax came from him and Josh Peek, another
|
||||
> early Rails-er.
|
||||
>
|
||||
> My focus was mostly on the user experience, the developer experience, and trying to stick to what made the web great.
|
||||
|
||||
- First commit: https://github.com/defunkt/jquery-pjax/commit/3efcc3c
|
||||
- X-PJAX: https://github.com/defunkt/jquery-pjax/commit/4367ec9
|
@ -1,20 +1,25 @@
|
||||
+++
|
||||
title = "An interview with Henning Koch, Creator of Unpoly"
|
||||
description = """\
|
||||
In this interview with Henning Koch, creator of Unpoly, he discusses his journey from managing a Rails consultancy \
|
||||
to developing this hypermedia-oriented JavaScript library. Koch shares insights on progressive enhancement, the \
|
||||
challenges of Single Page Applications, and why hypermedia approaches often deliver better results for typical web \
|
||||
applications."""
|
||||
date = 2022-06-13
|
||||
updated = 2023-06-13
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
I'm very excited to be able to interview Henning Koch, the creators of [Unpoly](https://unpoly.com/),
|
||||
I'm very excited to be able to interview Henning Koch, the creator of [Unpoly](https://unpoly.com/),
|
||||
a hypermedia-oriented javascript library that was created in parallel with intercooler.js.
|
||||
|
||||
Thank you for agreeing to an interview!
|
||||
|
||||
**Q**: To begin with, why don't you give the readers a bit of your background both professionally & technically:
|
||||
|
||||
> Sure! I'm currently head of development at [makandra](https://makandra.de/en), a Ruby on Rails consultancy I co-founded back in 2009, after many years of freelancing as a web developer. So my context is working on many different web apps concurrently, and maintaining those for a long time. On a given week we probably touch 10+ projects, for industries ranging from education to automative to cyber security. Unpoly is an extraction from patterns that we saw repeating over and over in client projects.
|
||||
> Sure! I'm currently head of development at [makandra](https://makandra.de/en), a Ruby on Rails consultancy I co-founded back in 2009, after many years of freelancing as a web developer. So my context is working on many different web apps concurrently, and maintaining those for a long time. On a given week we probably touch 10+ projects, for industries ranging from education to automotive to cybersecurity. Unpoly is an extraction from patterns that we saw repeating over and over in client projects.
|
||||
|
||||
**Q**: When I created intercooler.js a big part of it was my unwillingness to deal with the popular SPA libraries of the time
|
||||
(Angular & ExtJS, for example). Did Unpoly have a similar history?
|
||||
@ -30,7 +35,7 @@ a Rails developer too. Did that influence your approach to Unpoly?
|
||||
>
|
||||
> Some recent Rails mottos are "Compress the complexity of modern web apps" and "The one person framework". With my other responsibility at makandra being training young developers, that resonates with me a lot. I really care about maintaining a stack where a single person can be a full-stack developer and deliver good results consistently.
|
||||
>
|
||||
> Also, as a Rubyist, I have an excessive obsession with the ergonomics and aesthetics of code *as it is invoked*. I stress a lot over how a feature looks when it is used in client code. When a small ideas takes a disproportionate amount of code, this is something I lose sleep over.
|
||||
> Also, as a Rubyist, I have an excessive obsession with the ergonomics and aesthetics of code *as it is invoked*. I stress a lot over how a feature looks when it is used in client code. When a small idea takes a disproportionate amount of code, this is something I lose sleep over.
|
||||
|
||||
**Q**: Did you think much about hypermedia, REST, etc. when you were building Unpoly? Do you find that stuff useful? Interesting?
|
||||
|
||||
|
198
www/content/essays/interviews/leonard_richardson.md
Normal file
198
www/content/essays/interviews/leonard_richardson.md
Normal file
@ -0,0 +1,198 @@
|
||||
+++
|
||||
title = "An interview with Leonard Richardson"
|
||||
description = """\
|
||||
In this interview, Leonard Richardson, creator of the Richardson Maturity Model for RESTful web services discusses \
|
||||
the history of REST, interoperability in APIs and his impression of newer approaches such as GraphQL."""
|
||||
date = 2025-02-19
|
||||
updated = 2025-02-19
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
Leonard Richardson is a long time programmer and author and was the creator of what came to be termed the Richardson
|
||||
Maturity
|
||||
Model ([https://en.wikipedia.org/wiki/Richardson\_Maturity\_Model](https://en.wikipedia.org/wiki/Richardson_Maturity_Model)),
|
||||
a system for classifying Web APIs in terms of their adherence to REST. Here, Web APIs mean *data APIs*, that is data
|
||||
intended to be consumed by automated systems or code, rather than directly by a web client.
|
||||
|
||||
The RMM consists of four levels:
|
||||
|
||||
* Level 0 \- The Swamp of POX (Plain old XML)
|
||||
* Level 1 \- The appropriate use of resource-based URLs
|
||||
* Level 2 \- The appropriate use of HTTP Methods
|
||||
* Level 3 \- Hypermedia Controls
|
||||
|
||||
A Web API became more mature as it adopted these technologies and conventions.
|
||||
|
||||
Leonard agreed to talk to me about the RMM and his experiences building Web software.
|
||||
|
||||
**Question**: Can you give us some background about yourself and how you came into web programming? Did/do you consider
|
||||
yourself a hypermedia enthusiast?
|
||||
|
||||
> When I was in high school in the mid-1990s, I got very basic Internet access through BBSes. There were all these
|
||||
amazing, arcane protocols you had to learn to get around: FTP, Gopher, Archie, NNTP, et cetera. And then just as I went
|
||||
to college, the World Wide Web suddenly wiped out all of those domain-specific protocols. Within a couple of years we
|
||||
were using the Web technologies–URI, HTTP and HTML–for everything.
|
||||
>
|
||||
> My formative years as a developer happened against the background of the incredible power of those three core
|
||||
technologies. Everything was being built on top of the Web, and it basically worked and was a lot simpler than the old
|
||||
way.
|
||||
>
|
||||
> So yes, I am a hypermedia enthusiast, but it took me a really long time to understand the distinct advantages that come
|
||||
from the "hypermedia" part of the Web. And that came with an understanding of when those advantages are irrelevant or
|
||||
undesirable from a business standpoint.
|
||||
|
||||
**Question**: Can you give us a brief history of early Web APIs? What was the origin story of the RMM?
|
||||
|
||||
> What we now call "web APIs" started as a reaction against SOAP, a technology from a time when corporate firewalls
|
||||
allowed HTTP connections (necessary to get work done) but blocked most other traffic (games?). SOAP let you serialize a
|
||||
procedure call into XML and invoke it over an HTTP connection, punching through the firewall. It was an extraordinarily
|
||||
heavyweight solution, using the exact same tools–XML and HTTP–you'd need to make a good lightweight solution.
|
||||
>
|
||||
> Now that I'm an old fogey, I can look back on SOAP and see the previous generation of old fogeys trying to make the
|
||||
1990s client-server paradigm work over the Internet. SOAP had a lot of mindshare for a while, but there were very few
|
||||
publicly deployed SOAP services. When you deploy a service on the public Internet, people expect to connect to it from a
|
||||
wide variety of programming languages and programming environments. SOAP wasn't cut out for that because it was so heavy
|
||||
and demanded so much tooling to compensate for its heaviness.
|
||||
>
|
||||
> Instead, developers picked and chose their designs from the core web technologies. Thanks to the dot-com boom, those
|
||||
technologies were understood by practicing developers and well supported by every major programming language.
|
||||
>
|
||||
> The RMM, as it's now called, was originally a heuristic I presented
|
||||
in [a talk in 2008](https://www.crummy.com/writing/speaking/2008-QCon/act3.html). [The first part of the talk](https://www.crummy.com/writing/speaking/2008-QCon/act1.html)
|
||||
goes over the early history I mentioned earlier,
|
||||
and [the second part](https://www.crummy.com/writing/speaking/2008-QCon/act2.html) talks about my first experience
|
||||
trying to sell hypermedia-based API design to an employer.
|
||||
>
|
||||
> I’d analyzed about a hundred web API designs for my book on REST and seen very strong groupings around the core web
|
||||
technologies. You'd see a lot of APIs that "got" URLs but didn't "get" HTTP. But you'd never see one where it happened
|
||||
the other way, an API that took advantage of the features of HTTP but didn't know what to do with URLs. If I had one
|
||||
insight here, it's that the URL is the most fundamental web technology. HTTP is a set of rules for efficiently dealing
|
||||
with URLs, and HTML (a.k.a. hypermedia) is a set of instructions for driving an HTTP client.
|
||||
|
||||
**Question**: In “How Did REST come to mean the opposite of REST?” I assert that the term REST has nearly inverted in
|
||||
its meaning. In particular, I claim that most APIs stopped at “Level 2” of the RMM. Do you agree with these claims?
|
||||
|
||||
> Everyone understands URIs these days, and understanding HTTP is essential for performance reasons if nothing else. That
|
||||
gets you to level 2, and yes, there we have stayed. That's what I was getting at
|
||||
in [this interview from 2007](https://www.infoq.com/articles/richardson-ruby-restful-ws/), a year before I gave my
|
||||
famous talk:
|
||||
>
|
||||
> The big question in my mind is whether architectures consciously designed with REST in mind will “win” over
|
||||
architectures that are simple but only intermittently RESTful.
|
||||
>
|
||||
> You don't get a certificate signed by Roy Fielding for achieving level 3\. The reward for learning and applying the
|
||||
lesson of hypermedia is *interoperability*. Your users get the ability to use your system in ways you didn't anticipate,
|
||||
and they get to combine your system with their other systems.
|
||||
>
|
||||
> Interoperability is essential in a situation like the Web, where there are millions of server and client deployments,
|
||||
and a dozen major server implementations. (There are now only two major client implementations, but that's its own sad
|
||||
story.)
|
||||
>
|
||||
> For a long time I thought people just didn't get this and if I hammered on the technical advantages of hypermedia they'd
|
||||
come around. But we've been stuck at level 2 for more than half the lifetime of the Web. It's become clear to me that
|
||||
most situations aren't like the Web, and the advantages of hypermedia aren't relevant to most business models.
|
||||
|
||||
**Question**: Level 3 style hypermedia controls never really took off in Web APIs. Why do you think that is?
|
||||
|
||||
> I don't do this for everything, but I am going to blame this one on capitalism.
|
||||
>
|
||||
> Almost all actually deployed web APIs are under the complete control of one company. They have one server implementation
|
||||
written by that company and one server deployment managed by that company. If the API has any official client
|
||||
implementations, those are also controlled by the company that owns the API. The fact that we say "the \[company\] API"
|
||||
is the opposite of interoperability.
|
||||
>
|
||||
> Users like interoperability, but vendors prefer lock-in. We see that in their behavior. Netflix was happy to provide a
|
||||
hypermedia API for their program data... until their streaming business became big enough. Once they were the dominant
|
||||
player and could dictate the terms of integration, Netflix shut down their API. Twitter used to cut off your API access
|
||||
if your client got too popular; then they banned third-party clients altogether.
|
||||
>
|
||||
> There are lots of APIs that consider interoperability a strong point, and many of them are oriented around hypermedia,
|
||||
but almost all of them live outside the space of commercial competition. In 2008 when I gave the "maturity heuristic"
|
||||
talk I was working on software development tools at Canonical, which is a for-profit company but heavily involved in
|
||||
open source development. We wanted lots of tools and programming environments to be able to talk to our API, but the API
|
||||
was a relatively small part of our process and we didn't have enough developers to manage a bunch of official clients. A
|
||||
hypermedia-based approach gave us a certain flexibility to change our API without breaking all the clients.
|
||||
>
|
||||
> After that I spent eight years working on ebook delivery in the US public library space, which is extremely fragmented
|
||||
in terms of IT management. In a nonprofit environment with lots of independent server deployments, hypermedia (in the
|
||||
form of the [OPDS](https://opds.io/) protocol) was a really easy
|
||||
pitch. [I gave a talk about that.](https://www.crummy.com/writing/speaking/2015-RESTFest/)
|
||||
>
|
||||
> To get the benefits of hypermedia you have to collaborate with other people in the same field, consider the entire
|
||||
problem space, and come up with a design that works for everyone. Who's going to go through all that work when the
|
||||
reward is “no vendor lock-in”? People who are not competing with their peers: scientists, librarians, and open source
|
||||
developers.
|
||||
>
|
||||
> It might or might not surprise you to learn that the library world is dominated by an antique protocol
|
||||
called [SIP](https://developers.exlibrisgroup.com/wp-content/uploads/2020/01/3M-Standard-Interchange-Protocol-Version-2.00.pdf). (
|
||||
Not the VoIP protocol, a different SIP.) SIP is what the self-checkout machine uses to record the fact that you borrowed
|
||||
the book. SIP first showed up in 1993, its design is distinctively non-RESTful, and in many ways it’s simply *bad*.
|
||||
Every library vendor has come up with their own level 2 "REST" protocol to do what SIP does. But none have succeeded in
|
||||
displacing SIP, because that awful old 1993 design provides something a vendor can't offer: interoperability between
|
||||
components from different
|
||||
vendors. [I gave a talk about that, too.](https://www.crummy.com/writing/speaking/2016-RESTFest/)
|
||||
|
||||
**Question**: Do you think the move from XML to JSON as a format had any influence on how Web APIs evolved?
|
||||
|
||||
> Absolutely. Moving from XML to JSON replaced a document-centric design (more suitable for communications with a human at
|
||||
one end) with a data-centric design (more suitable for machine-to-machine communication). The cost was forgetting about
|
||||
hypermedia altogether.
|
||||
>
|
||||
> One thing about Martin's diagram that I think obscures more than it reveals is: he calls level 0 the "Swamp of POX".
|
||||
This makes it seem like the problem is (Plain Old) XML. Martin is actually talking about SOAP there. The big problem
|
||||
with SOAP services isn't XML (although they do have way too much XML), it's that they don't use URLs. A SOAP client puts
|
||||
all of the request information into an XML package and tunnels it through a single service endpoint. This makes SOAP
|
||||
opaque to the tools designed to manage and monitor and inspect HTTP traffic. This is by design, because the point of
|
||||
SOAP is to let you make RPC calls when your IT department has everything but port 80 locked down.
|
||||
>
|
||||
> Anyway, XML is great\! It's too verbose to make an efficient data representation format, but XML has namespaces, and
|
||||
through namespaces it has hypermedia controls (via XLink, XForms, XHTML, Atom, etc.). JSON has no hypermedia controls,
|
||||
and because it also has no namespaces, you can't add them after the fact.
|
||||
>
|
||||
> People started adopting JSON because they were tired of XML processing and excited about AJAX (in-browser HTTP clients
|
||||
driven by Javascript, for those who weren't there). But that initial decision started constraining decisions down the
|
||||
road.
|
||||
>
|
||||
> By 2011, all new web APIs were using a representation format with no hypermedia controls. You couldn't do a
|
||||
hypermedia-based design if you wanted to. Our technical language had lost the words. First you'd have to define a
|
||||
JSON-based media type that had hypermedia (like Siren), or namespaces (like JSON-LD).
|
||||
|
||||
**Question**: What are your thoughts on GraphQL and other non-RESTful API technologies?
|
||||
|
||||
> With regard to non-RESTful API technologies in general, I would suggest that folks take a break
|
||||
from [chapter 5](https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) of Roy Fielding's dissertation,
|
||||
and look at chapters [2](https://ics.uci.edu/~fielding/pubs/dissertation/net_app_arch.htm%20)
|
||||
and [4](https://ics.uci.edu/~fielding/pubs/dissertation/web_arch_domain.htm).
|
||||
>
|
||||
> Chapter 5 is where Fielding talks about the design of the Web, but Chapter 2 breaks down all of the possible good things
|
||||
you might want from a networked application architecture, only some of which apply to the Web. Chapter 4 explains the
|
||||
tradeoffs that were made when designing the Web, giving us some good things at the expense of others.
|
||||
>
|
||||
> Chapter 4 lists five main advantages of the Web: low entry-barrier, extensibility, distributed hypermedia, anarchic
|
||||
scalability, and independent deployment. REST is a really effective way of getting those advantages, but the advantages
|
||||
themselves are what you really want. If you can get them without the Web technologies, then all you've lost is the
|
||||
accumulated expertise that comes with those technologies (although that ain't nothing at this point). And if you *don't*
|
||||
want some of these advantages (probably distributed hypermedia) you can go back to chapter 2, start the process over,
|
||||
and end up with a differently optimized architecture.
|
||||
>
|
||||
> I don't have any direct experience with GraphQL, though I'm about to get some at my current job, so take this with a
|
||||
grain of salt:
|
||||
>
|
||||
> On a technical level, GraphQL is solving a problem that's very common in API design: performing a database query across
|
||||
a network connection without sending a bunch of unneeded data over the wire. Looking at the docs I see it also has "
|
||||
Mutations" which seem very SOAP-ish. I guess I'd say GraphQL looks like a modern version of SOAP, optimized for the
|
||||
common case of querying a database.
|
||||
>
|
||||
> Since GraphQL is independently deployable, supports multiple server implementations and defines no domain-specific
|
||||
semantics, an interoperable domain-specific API could be built on top of it. Rather than exporting your data model to
|
||||
GraphQL and clashing with a dozen similar data models from the same industry, you could get together with your peers and
|
||||
agree upon a common set of semantics and mutations for your problem space. Then you'd have interoperability. It's not
|
||||
much different from what we did with OPDS in the library world, defining what concepts like "bookshelf" and "borrow"
|
||||
mean.
|
||||
>
|
||||
> Would it be RESTful? Nope\! But again I'll come back to SIP, the integration protocol that public libraries use to keep
|
||||
track of loans. SIP is a level zero protocol\! It doesn't use any of the Web technologies at all\! But it provides
|
||||
architectural properties that libraries value and vendor-centric solutions can't offer–mainly interoperability–so it
|
||||
sticks around despite the presence of "RESTful" solutions.
|
55
www/content/essays/interviews/makinde_adeagbo.md
Normal file
55
www/content/essays/interviews/makinde_adeagbo.md
Normal file
@ -0,0 +1,55 @@
|
||||
+++
|
||||
title = "An interview with Makinde Adeagbo, Creator of Primer"
|
||||
description = """\
|
||||
In this interview with software engineer Makinde Adeagbo, he discusses his role in creating Primer, a \
|
||||
hypermedia-oriented JavaScript library used at Facebook in the 2000s, and shares insights about building developer \
|
||||
tools, Facebook's evolution, and the cyclical nature of web development approaches."""
|
||||
date = 2025-01-27
|
||||
updated = 2025-01-27
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
I'm delighted to be able to interview Makinde Adeagbo, one of the creators of [Primer](https://www.youtube.com/watch?v=wHlyLEPtL9o),
|
||||
an hypermedia-oriented javascript library that was being used at Facebook in the 2000s.
|
||||
|
||||
Thank you for agreeing to an interview!
|
||||
|
||||
Q: To begin with, why don’t you give the readers a bit of your background both professionally & technically?
|
||||
|
||||
>I’ve always been into tech. In high school, I used to build computers for friends and family. I took the computer science classes my high school offered and went on to study computer science in college. I was always amazed by the fact that I could build cool things—games, tools, etc.—with just a computer and an internet connection.
|
||||
>
|
||||
>I was lucky enough to participate in Explore Microsoft, an internship that identifies underrepresented college freshmen and gives them a shot at working at Microsoft. After that experience, I was sold on software as my future. I later interned at Apple and Microsoft again. During college, I also worked at Facebook when the company was about 150 employees. It was an incredible experience where engineers had near-total freedom to build and contribute to the company’s growth. It was exactly what I needed early in my career, and I thrived. From there, I went on to work at Dropbox and Pinterest and also co-founded the nonprofit, /dev/color.
|
||||
|
||||
Q: Can you give me the history of how Primer came to be?
|
||||
|
||||
>In 2010, the Facebook website was sloooow. This wasn’t the fault of any specific person—each engineer was adding features and, along the way, small amounts of JavaScript. However, we didn’t have a coherent system for sharing libraries or tracking how much JavaScript was being shipped with each page. Over time, this led to the 90th-percentile page load time ballooning to about 10 seconds! Midway through the year, reducing that load time by half became one of the company’s three top priorities. I was on a small team of engineers tasked with making it happen.
|
||||
>
|
||||
>As we investigated where most of the JavaScript was coming from, we noticed the majority of it was performing simple tasks. These tasks involved either fetching additional data or markup from the server, or submitting a form and then receiving more markup to update the page. With limited time, we decided to build a small solution to abstract those patterns and reduce the amount of code needed on the page.
|
||||
>
|
||||
>Tom Occhino and I built the first version of Primer and converted a few use cases ourselves to ensure it worked well. Once we were confident, we brought more engineers into the effort to scale it across the codebase.
|
||||
|
||||
Q: Primer & React were both created at Facebook. Was there any internal competition or discussion between the teams? What did that look like?
|
||||
|
||||
>The two projects came from different eras, needs, and parts of the codebase. As far as I know, there was never any competition between them.
|
||||
>
|
||||
>Primer worked well for the type of website we were building in 2010. A key part of its success was understanding that it wasn’t meant to handle every use case. It was an 80/20 solution, and we didn’t use it for particularly complex interactions (like the interface for creating a new post).
|
||||
>
|
||||
>React emerged from a completely different challenge: the ads tools. Managing, composing, and tracking hundreds of ads required a highly involved, complex interface. I’m not sure if they ever attempted to use Primer for it, but it would have been a miserable experience. We didn’t have the terminology at the time, but this was a classic example of a single-page application needing purpose-built tools. The users of that site also had a very different profile from someone browsing their home feed or clicking through photos.
|
||||
|
||||
Q: Why do you think Primer ultimately failed at Facebook?
|
||||
|
||||
>I don’t think there’s any single technical solution that has spanned 15 years in Facebook’s platform. The site’s needs evolve, technology changes, and the internet’s landscape shifts over time. Primer served the site well for its time and constraints, but eventually, the product demanded richer interactivity, which wasn’t what Primer was designed for.
|
||||
>
|
||||
>Other tradeoffs also come into play: developer ease/speed, security, scalability. These priorities and tradeoffs change over time, especially as a company grows 10x in size.
|
||||
>
|
||||
>More broadly, these things tend to work in cycles in the industry. Streamlined, fast solutions give way to richer, heavier tools, which eventually cycle back to streamlined and fast. I wouldn’t be surprised if something like Primer made a comeback at some point.
|
||||
|
||||
Q: How much “theory” was there to Primer? Did you think much about hypermedia, REST, etc., when you were building it?
|
||||
|
||||
>Not much. Honestly, I was young and didn’t know a ton about the internet’s history or past research. I was drawn to the simplicity of the web’s underlying building blocks and thought it was fun to use those tools as they were designed. But, as always, the web is a layer cake of hacks and bandaids, so you have to be flexible.
|
||||
|
||||
Q: What were the most important technical lessons you took away from Primer?
|
||||
|
||||
>Honestly, the biggest lessons were about people. Building a system like Primer is one thing, but for it to succeed, you have to train hundreds of engineers to use it. You have to teach them to think differently about building things, ask questions at the right time, and avoid going too far in the wrong direction. At the end of the day, even if the system is perfect, if engineers hate using it, it won’t succeed.
|
158
www/content/essays/interviews/mike_amundsen.md
Normal file
158
www/content/essays/interviews/mike_amundsen.md
Normal file
@ -0,0 +1,158 @@
|
||||
+++
|
||||
title = "An interview with Mike Amundsen, Author of 'RESTful Web APIs'"
|
||||
description = """\
|
||||
In this in-depth interview, Mike Amundsen, a leading expert on REST and hypermedia, discusses the evolution of \
|
||||
hypermedia technologies, highlights unsung pioneers like Paul Otlet, and shares insights on the future of hypermedia \
|
||||
in enterprise systems and machine-to-machine interactions."""
|
||||
date = 2025-01-27
|
||||
updated = 2025-01-27
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
Mike Amundsen is a computer programmer, author and speaker, and is one of the world leading experts on REST &
|
||||
hypermedia. He has been writing about REST and Hypermedia since 2008 and has published two books on the ideas:
|
||||
|
||||
* [RESTful Web APIs](http://restfulwebapis.com/)
|
||||
* [Building Hypermedia APIs with HTML and Node](http://www.dpbolvw.net/click-7269430-11260198?sid=HP&url=http%3A%2F%2Fshop.oreilly.com%2Fproduct%2F0636920020530.do%3Fcmp%3Daf-prog-book-product_cj_9781449306571_%25zp&cjsku=0636920020530)
|
||||
|
||||
Mike agreed to do an interview with me on his view of the history of hypermedia and where things are today.
|
||||
|
||||
**Q**: The “standard” history of hypermedia is Vannevar Bush’s “As We May Think”, followed by Nelson introducing
|
||||
the term “hypermedia” in 1963, Englebart’s “Mother of all Demos” in 1968 and then Berners-Lee creating The Web in 1990\.
|
||||
Are there any other important points you see along the way?
|
||||
|
||||
> I think starting the history of what I call the “modern web” with Bush makes a lot of sense. Primarily because you can
|
||||
> directly link Bush to Engelbart to Nelson to Berners-Lee to Fielding. That’s more than half a century of scholarship,
|
||||
> design, and implementation that we can study, learn from, and expand upon.
|
||||
>
|
||||
> At the same time, I think there is an unsung hero in the hypermedia story; one stretches back to the early 20th century.
|
||||
> I am referring to the Belgian author and entrepreneur [Paul Otlet](https://en.wikipedia.org/wiki/Paul_Otlet). Otlet had
|
||||
> a vision of [a multimedia information system](https://monoskop.org/Mundaneum_symposium) he named the “World Wide
|
||||
> Network”. He saw how we could combine text, audio, and video into a mix of live and on-demand replay of content from
|
||||
> around the world. He even envisioned a kind of multimedia workstation that supported searching, storing, and playing
|
||||
> content in what was the earliest instance I can find of an understanding of what we call “streaming services” today.
|
||||
>
|
||||
> To back all this up, he
|
||||
> created [a community of researchers](https://daily.jstor.org/internet-before-internet-paul-otlet/) that would read
|
||||
> monographs, articles, and books then summarize them to fit on a page or less. He then designed an identification
|
||||
> system – much like our URI/URN/URLs today and created a massive card catalog system to enable searching and collating
|
||||
> the results into a package that could be shared – even by postal service – with recipients. He created web search by
|
||||
> mail in the 1920s\!
|
||||
>
|
||||
> This was a man well ahead of his time that I’d like to see talked about more in hypermedia and information system
|
||||
> circles.
|
||||
|
||||
**Question**: Why do you think that The Web won over other hypermedia systems (such as Xanadu)?
|
||||
|
||||
> The short reason is, I think, that Xanadu was a much more detailed and specific way of thinking about linking documents,
|
||||
> documenting provenance, and compensating authors. That’s a grand vision that was difficult to implement back in the 60s
|
||||
> and 70s when Nelson was sharing his ideas.
|
||||
>
|
||||
> There are, of course, lots of other factors. Berners-Lee’s vision was much smaller (he was trying to make it easy for
|
||||
> CERN staff to share contact information\!). Berners-Lee was, I think, much more pragmatic about the implementation
|
||||
> details. He himself said he used existing tech (DNS, packet networking, etc.) to implement his ideas. That meant he
|
||||
> attracted interest from lots of different communities (telephone, information systems, computing, networking, etc.).
|
||||
>
|
||||
> I would also say here that I wish [Wendy Hall](https://en.wikipedia.org/wiki/Wendy_Hall)
|
||||
> ’s [Microcosm](https://www.sciencefriday.com/articles/the-woman-who-linked-the-web-in-a-microcosm/) had gotten more
|
||||
> traction than it did. Hall and her colleagues built an incredibly rich hypermedia system in the 90s and released it
|
||||
> before Berners-Lee’s version of “the Web” was available. And Hall’s Microcosm held more closely to the way Bush,
|
||||
> Englebart, and Nelson thought hypermedia systems would be implemented – primarily by storing the hyperlinks in a
|
||||
> separate “anchor document” instead of in the source document itself.
|
||||
|
||||
**Question**: What do you think of my essay “How did REST come to mean the opposite of REST”? Are there any points you
|
||||
disagree with in it?
|
||||
|
||||
> I read that piece back in 2022 when you released it and enjoyed it. While I have nothing to quibble with, really, there
|
||||
> are a few observations I can share.
|
||||
>
|
||||
> I think I see most hypermedia developers/researchers go through a kind of cycle where you get exposed to “common” REST,
|
||||
> then later learn of “Fielding's REST” and then go back to the “common REST” world with your gained knowledge and try to
|
||||
> get others on board; usually with only a small bit of success.
|
||||
>
|
||||
> I know you like memes so, I’ll add mine here. This journey away from home, into expanded knowledge and the return to the
|
||||
> mundane life you once led is – to me – just another example of Campbell’s Hero’s Journey\<g\>. I feel this so strongly
|
||||
> that I created [my own Hero’s Journey presentation](http://amundsen.com/talks/2015-05-barcelona/index.html) to deliver
|
||||
> at API conferences over the years.
|
||||
>
|
||||
> On a more direct note. I think many readers of Fielding's Dissertation (for those who actually read it) miss some key
|
||||
> points. Fielding’s paper is about designing network architecture, not about REST. REST is offered as a real-world
|
||||
> example but it is just that; an example of his approach to information network design. There have been other designs
|
||||
> from the same school (UC Irvine) including Justin Erenkrantz’s Computational
|
||||
> REST ([CREST](https://www.erenkrantz.com/CREST/)) and Rohit Kare’s Asynchronous REST (A-REST). These were efforts that
|
||||
> got the message of Fielding: “Let’s design networked software systems\!”
|
||||
>
|
||||
> But that is much more abstract work that most real-world developers need to deal with. They have to get code out the
|
||||
> door and up and running quickly and consistently. Fielding’s work, he admitted, was on
|
||||
> the “[scale of decades](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven#comment-724)” – a scale
|
||||
> most developers are not paid to consider.
|
||||
>
|
||||
> In the long run, I think it amazing that a PhD dissertation from almost a quarter-century ago has had such a strong
|
||||
> influence on day-to-day developers. That’s pretty rare.
|
||||
|
||||
**Question**: Hyperview, the mobile hypermedia that Adam Stepinski created, was very explicitly based on your books.
|
||||
Have you looked at his system?
|
||||
|
||||
> I have looked over [Hyperview](https://hyperview.org/) and like what I see. I must admit, however, that I don’t write
|
||||
> mobile code anymore so I’ve not actually written any hyperview code myself. But I like it.
|
||||
>
|
||||
> I talked to Adam in 2022 about Hyperview in general and was impressed with his thoughts. I’d like to see more people
|
||||
> talking about and using the Hyperview approach.
|
||||
>
|
||||
> Something I am pretty sure I mentioned to Adam at the time is that Hyperview reminds me of Wireless Markup
|
||||
> Language ([WML](https://en.wikipedia.org/wiki/Wireless_Markup_Language)). This was another XML-based document model
|
||||
> aimed at rendering early web content on feature phones (before smartphone technology). Another XML-based hypermedia
|
||||
> domain-specific document format is [VoiceXML](https://en.wikipedia.org/wiki/VoiceXML). I still think there are great
|
||||
> applications of hypermedia-based domain-specific markup languages (DSML) and would like to see more of them in use.
|
||||
|
||||
**Question**: It’s perhaps wishful thinking, but I feel there is a resurgence in interest in the ideas of hypermedia and
|
||||
REST (real REST.) Are you seeing this as well? Do you have a sense if businesses are starting to recognize the
|
||||
strengths of this approach?
|
||||
|
||||
> I, myself, think there is a growth in hypermedia-inspired designs and implementations and I’m glad to see it. I think
|
||||
> much of the work of APIs in general has been leading the market to start thinking about how to lower the barrier of
|
||||
> entry for using and interoperating with remote, independent services. And the hypermedia control paradigm (the one you
|
||||
> and your colleagues talk about in your
|
||||
> paper “[Hypermedia Controls: Feral to Formal](https://dl.acm.org/doi/fullHtml/10.1145/3648188.3675127)”) offers an
|
||||
> excellent way to do that.
|
||||
>
|
||||
> I think the biggest hurdle for using more hypermedia in business is
|
||||
> was [laid out pretty conclusively](https://www.crummy.com/writing/speaking/2015-RESTFest/)
|
||||
> by [Leonard Richardson](https://www.crummy.com/self/) several years ago. He helped build a
|
||||
> powerful [hypermedia-based book-sharing server and client](https://opds.io/) system to support public libraries around
|
||||
> the world. He noted that, in the library domain, each site is not a competitor but a partner. That means libraries are
|
||||
> encouraged to make it easier to loan out books and interoperate with other libraries.
|
||||
>
|
||||
> Most businesses operate on the opposite model. They typically succeed by creating barriers of entry and by hoarding
|
||||
> assets, not sharing them. Hypermedia makes it easier to share and interact without the need of central control or other
|
||||
> types of “gatekeeping.”
|
||||
>
|
||||
> Having said that, I think a ripe territory for increased use of hypermedia to lower the bar and increase interaction is
|
||||
> at the enterprise level in large organizations. Most big companies spend huge amounts of money building and rebuilding
|
||||
> interfaces in order to improve their internal information system. I can’t help but think designing and implementing
|
||||
> hypermedia-driven solutions would yield long-term savings, and near-term sustainable interoperability.
|
||||
|
||||
**Question**: Are there any concepts in hypermedia that you think we are sleeping on? Or, maybe said another way, some
|
||||
older ideas that are worth looking at again?
|
||||
|
||||
> Well, as I just mentioned, I think hypermedia has a big role to play in the field of interoperability. And I think the
|
||||
> API-era has, in some ways, distracted us from the power of hypermedia controls as a design element for
|
||||
> service-to-service interactions.
|
||||
>
|
||||
> While I think Nelson, Berners-Lee and others have done a great job of laying out the possibilities for human-to-machine
|
||||
> interaction, I think we’ve lost sight of the possibilities hypermedia gives us for machine-to-machine interactions. I am
|
||||
> surprised we don’t have more hypermedia-driven workflow systems available today.
|
||||
>
|
||||
> And I think the rise in popularity of LLM-driven automation is another great opportunity to create hypermedia-based,
|
||||
> composable services that can be “orchestrated” on the fly. I am worried that we’ll get too tied up in trying to make
|
||||
> generative AI systems look and act like human users and miss the chance to design hypermedia workflow designed
|
||||
> specifically to take advantage of the strengths of statistical language models.
|
||||
>
|
||||
> I’ve seen some interesting things in this area including [Zdenek Nemec](https://www.linkedin.com/in/zdne/)’s
|
||||
> [Superface](https://superface.ai/) project which has been working on this hypermedia-driven workflow for several
|
||||
> years.
|
||||
>
|
||||
> I just think there are lots of opportunities to apply what we’ve learned from the last 100 years (when you include
|
||||
> Otlet) of hypermedia thinking. And I’m looking forward to seeing what comes next.
|
@ -1,8 +1,14 @@
|
||||
+++
|
||||
title = "Is htmx Just Another JavaScript Framework?"
|
||||
description = """\
|
||||
Alexander Petros give a thoughtful exploration of htmx's relationship to traditional JavaScript frameworks, \
|
||||
examining how its HTML-first approach and narrow focus on network requests sets it apart. He argues that while htmx \
|
||||
exhibits framework-like qualities in how it shapes application architecture, its deep integration with HTML's native \
|
||||
capabilities and lack of dependencies makes it a more sustainable choice for building long-lasting web \
|
||||
applications."""
|
||||
date = 2024-01-10
|
||||
authors = ["Alexander Petros"]
|
||||
[taxonomies]
|
||||
author = ["Alexander Petros"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "Locality of Behaviour (LoB)"
|
||||
description = """\
|
||||
Carson Gross explores the Locality of Behaviour (LoB) principle, which emphasizes making the behavior of code units \
|
||||
obvious on inspection to enhance maintainability. He discusses the tradeoffs between LoB and other software design \
|
||||
principles like DRY and SoC, offering insights on balancing clarity, abstraction, and maintainability in modern \
|
||||
development."""
|
||||
date = 2020-05-29
|
||||
updated = 2023-01-20
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
178
www/content/essays/lore.md
Normal file
178
www/content/essays/lore.md
Normal file
@ -0,0 +1,178 @@
|
||||
+++
|
||||
title = "htmx lore"
|
||||
description = """\
|
||||
Carson Gross explores the fascinating lore of htmx, from its playful community memes like "It's So Over/We're \
|
||||
So Back" and "Laser Eye Horse" to humorous controversies such as the "Microsoft Purchase \
|
||||
Rumor" and the htmx/intercooler.js feud. This essay dives into the vibrant culture, iconic moments, and \
|
||||
lighthearted chaos surrounding htmx."""
|
||||
date = 2024-12-17
|
||||
updated = 2024-12-17
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
<center style="padding: 12px">
|
||||
<img src="/img/i-lied.png" alt="I lied." style="border-radius: 32px; max-width: 100%">
|
||||
</center>
|
||||
|
||||
For better or [for worse](https://x.com/IroncladDev/status/1866185587616596356), htmx has collected a lot of lore, mainly around [the twitter account](https://twitter.com/htmx_org).
|
||||
|
||||
Here are some explanations.
|
||||
|
||||
## It's So Over/We're So Back
|
||||
|
||||
A common set of phrases used by htmx enthusiasts when, for example, [@bunjavascript told me to delete my account](https://x.com/bunjavascript/status/1708557665268568412)
|
||||
|
||||
## htmx CEO
|
||||
|
||||
At one point there was a hostile takeover attempt of the htmx CEO position and, in a desperate poison pill, I declared
|
||||
everyone CEO of htmx.
|
||||
|
||||
[Turk](https://x.com/gitpush_gitpaid) created <https://htmx.ceo> if you want to register as a CEO.
|
||||
|
||||
If someone emails hr@bigsky.software asking if you are CEO of htmx, I will tell them yes.
|
||||
|
||||
You can put it on your LinkedIn, because it's true.
|
||||
|
||||
## Laser Eye Horse
|
||||
|
||||
At some point I photoshopped lasers onto a horse mask, as kind of an homage to [@horse_js](https://x.com/horse_js).
|
||||
|
||||
For some reason it stuck and now it's the [official unofficial](https://swag.htmx.org/products/i-lied-sticker) mascot of htmx.
|
||||
|
||||
## Spieltrieb
|
||||
|
||||
Spieltrieb means "play instinct", and is a big part of the [htmx vibe](https://x.com/search?q=spieltrieb%20from%3Ahtmx_org&src=typed_query).
|
||||
|
||||
## Pickles
|
||||
|
||||
At some point someone (I think [@techsavvytravvy](https://x.com/techsavvytravvy)), generated [a grug AI image](https://x.com/htmx_org/status/1708697536587047142), and there
|
||||
was a pickle smiling in a really bizarre way in it.
|
||||
|
||||
So we started riffing on pickles and now [there's a shirt](https://swag.htmx.org/products/htmx-pickle-shirt).
|
||||
|
||||
Cry more, [drizzle](https://x.com/DrizzleORM/status/1757149983713665238).
|
||||
|
||||
## XSS
|
||||
|
||||
In July 2023, when htmx first got popular, there was a
|
||||
[moral panic](https://x.com/htmx_org/status/1683607693246775297) around
|
||||
[cross site scripting](https://x.com/htmx_org/status/1683529221195571200). I
|
||||
[may](https://x.com/htmx_org/status/1683607217499414531) have
|
||||
[overcooked](https://x.com/htmx_org/status/1683649190071791617) my
|
||||
[response](https://x.com/htmx_org/status/1683612179512057856) to
|
||||
[it](https://x.com/htmx_org/status/1683818711763877892).
|
||||
|
||||
## Shut Up Warren
|
||||
|
||||
[@WarrenInTheBuff](https://x.com/WarrenInTheBuff) is the king of twitter and we regularly fight with him. This often
|
||||
ends in someone saying ["shut up warren"](https://x.com/ThePrimeagen/status/1792564215749779515).
|
||||
|
||||
You can see the htmx website do this by going to <https://htmx.org?suw=true>
|
||||
|
||||
## Microsoft Purchase Rumor
|
||||
|
||||
In mid-January of 2024 I got really serious with the htmx twitter account and started [quote](https://x.com/htmx_org/status/1745930477825868044)
|
||||
tweeting [things](https://x.com/htmx_org/status/1745915394626351315)
|
||||
[about microsoft](https://x.com/htmx_org). People started [worrying](https://x.com/SusSoftware/status/1746206195461878113). I announced a [license change](https://x.com/htmx_org/status/1746736273728094323)
|
||||
to get people freaked out about a rug pull.
|
||||
|
||||
[I then changed htmx to BSD0](https://x.com/htmx_org/status/1746880860723544211)
|
||||
|
||||
[This is the offer](https://x.com/htmx_org/status/1746895016256328079) I got from microsoft (real).
|
||||
|
||||
## (same thing)
|
||||
|
||||
I believe that [this tweet](https://x.com/htmx_org/status/1672264927136952322) is the origin of the (same thing) meme
|
||||
|
||||
## Stronger Together
|
||||
|
||||
In December 2023, I was trying to get some indonesian twitter users to take a look at htmx, so I created a
|
||||
["Montana & Indonesia, Stronger Together!"](https://x.com/htmx_org/status/1734371865156563428) tweet w/an AI image.
|
||||
|
||||
This turned into a [whole series of tweets](https://x.com/search?q=%22stronger%20together%22%20from%3A%40htmx_org&src=typed_query&f=live).
|
||||
|
||||
## Hinges
|
||||
|
||||
Sometimes I am accused of being "unhinged" but, in fact, [I own many hinges](https://x.com/search?q=from%3Ahtmx_org%20hinges&src=typed_query).
|
||||
|
||||
## \* library
|
||||
|
||||
People often [call htmx a framework](@/essays/is-htmx-another-javascript-framework.md), but it's [a library](https://x.com/htmx_org/status/1848751101035827210)
|
||||
|
||||
## "man"
|
||||
|
||||
A common [one word response](https://x.com/search?q=%22man%22%20from%3Ahtmx_org&src=typed_query&f=live) when I don't feel like
|
||||
arguing with someone.
|
||||
|
||||
## The Le Marquee d'<something>
|
||||
|
||||
In December 2024, I [added a marquee tag](https://github.com/bigskysoftware/htmx/commit/2b88d967c19619281228d1bf5398751615bdf462) to
|
||||
the htmx website and started using the honorific (sic) in my twitter title.
|
||||
|
||||
## htmx sucks
|
||||
|
||||
I wrote an essay called [htmx sucks](@/essays/htmx-sucks.md) in which I criticize htmx (some valid, some tongue in
|
||||
cheek, most both.) I also released [a mug](https://swag.htmx.org/products/htmx-sucks-mug) that I will often link to when people are criticizing htmx.
|
||||
|
||||
## Jason Knight
|
||||
|
||||
Jason Knight [hates htmx](https://x.com/JasonKn99664124/status/1731555036864381251) and wrote a
|
||||
[great post](https://archive.is/rQrl7) about it.
|
||||
|
||||
Please don't harass him, [I draw energy](https://x.com/htmx_org/status/1756476449693872635) from his posts.
|
||||
|
||||
## Drop Downs
|
||||
|
||||
In July 2023, sparked by the accusation that htmx users could not create dropdowns, I did a deep-dive into web
|
||||
drop down technology and [uncovered a bombshell](https://x.com/htmx_org/status/1684936514885869568)
|
||||
|
||||
## "htmx is a front end library of peace"
|
||||
|
||||
A phrase I will often [quote tweet](https://x.com/search?q=htmx%20is%20a%20front%20end%20library%20of%20peace%20from%3A%40htmx_org&src=typed_query&f=live)
|
||||
violent htmx-related imagery with.
|
||||
|
||||
## The Process ™
|
||||
|
||||
[The Process™](https://x.com/htmx_org/status/1697651918858764375) is the mechanism by which people initially hostile
|
||||
to htmx come to be enlightened.
|
||||
|
||||
## "that's ridiculous"
|
||||
|
||||
In [June 2023](https://x.com/htmx_org/status/1807183339222405317), [@srasash](https://twitter.com/srasash) accused
|
||||
htmx of being a government op, the first in many such increasingly ridiculous claims. I typically quote-tweet these
|
||||
claims and point out that ["that's ridiculous"](https://x.com/search?q=%22that%27s%20ridiculous%22%20from%3A%40htmx_org&src=typed_query&f=live)
|
||||
|
||||
## Grug
|
||||
|
||||
I created <http://grugbrain.dev>.
|
||||
|
||||
## The htmx/intercooler.js feud
|
||||
|
||||
The htmx & [intercooler.js](https://x.com/intercoolerjs) twitter accounts often fight with one another. Sometimes its
|
||||
just me [switching back and forth](https://x.com/intercoolerjs/status/1859652045399355559), but two other people have
|
||||
access to the intercooler account, so sometimes I have no idea who I am fighting with.
|
||||
|
||||
## If Nothing Magically Works
|
||||
|
||||
Nothing [magically breaks](https://x.com/htmx_org/status/1729870461864226829).
|
||||
|
||||
## /r/webdev
|
||||
|
||||
I was very unfairly given [a lifetime ban](https://x.com/htmx_org/status/1719687461385691283) from
|
||||
[/r/webdev/](https://www.reddit.com/r/webdev/) for an
|
||||
[obviously satirical post](https://old.reddit.com/r/webdev/comments/17i0loi/anyone_heard_of_htmx/). Even the term "htmx" is banned (or semi-banned) on that sub, so people now use
|
||||
the [htmeggs](https://swag.htmx.org/products/htmeggs-shirt) instead.
|
||||
|
||||
## "looking into this"
|
||||
|
||||
[idk](https://x.com/search?q=%22idk%22%20from%3Ahtmx_org&src=typed_query&f=live), I just think [it's funny](https://x.com/search?q=%22looking%20into%20this%22%20from%3Ahtmx_org&src=typed_query)
|
||||
|
||||
## "Look at this nerd ragin'"
|
||||
|
||||
A common phrase used to [mock people (including ourselves)](https://x.com/search?q=%22Look%20at%20this%20nerd%20ragin%27%22%20from%3Ahtmx_org&src=typed_query) with.
|
||||
|
||||
## Joker/Bane/Skeletor/Thanos, etc.
|
||||
|
||||
htmx is a [villain](https://x.com/htmx_org/status/1651698199478796292) in the front-end world, I'm good w/that
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "Model/View/Controller (MVC)"
|
||||
description = """\
|
||||
Carson Gross give an introduction to the Model/View/Controller (MVC) design pattern and its relevance to modern web \
|
||||
development. He explores MVC concepts, its historical adoption in frameworks like Ruby on Rails, and practical \
|
||||
examples of its implementation in Python with Flask. Carson explains how separating concerns with MVC can reduce \
|
||||
code duplication, support both JSON APIs and hypermedia, and maintain flexibility in application design."""
|
||||
date = 2024-01-16
|
||||
updated = 2024-01-16
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,8 +1,14 @@
|
||||
+++
|
||||
title = "Why htmx Does Not Have a Build Step"
|
||||
description = """\
|
||||
In this essay, Alexander Petros explores the reasons why the htmx JavaScript library does not include a build step, \
|
||||
detailing the benefits of its simple, dependency-free structure. He highlights the longevity of plain JavaScript, \
|
||||
the improved debugging experience, enforced code clarity, and the tradeoffs of avoiding tools like TypeScript or \
|
||||
ES6. Alexander also acknowledges the potential limitations of this approach and discusses how these tradeoffs align \
|
||||
with htmx's goals of simplicity and developer choice in web development."""
|
||||
date = 2023-08-19
|
||||
authors = ["Alexander Petros"]
|
||||
[taxonomies]
|
||||
author = ["Alexander Petros"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -15,7 +21,7 @@ The best reason to write a library in plain JavaScript is that it lasts forever.
|
||||
|
||||
Of course, most people's experience with JavaScript is that it ages like milk. Reopen a node repository after 3 months and you'll find that your project is mired in a flurry of security warnings, backwards-incompatible library "upgrades," and a frontend framework whose cultural peak was the exact moment you started the project and is now widely considered tech debt. Who's to blame for this situation is for someone else to decide, but, in any case, you can eliminate this entire problem class by not having any dependencies beyond the JavaScript runtime.
|
||||
|
||||
A popular way to write JavaScript today is compile it from TypeScript (which I will use frequently as an example, because TypeScript is probably the [best reason](https://en.wikipedia.org/wiki/Straw_man#Steelmanning) to use a build system). TypeScript does not run natively in web browsers, so TypeScript code is not protected by [ECMA's](https://developer.mozilla.org/en-US/docs/Glossary/ECMA) fanatical devotion to backwards compatibility. Like any dependency, new major TypeScript versions are not guaranteed to be backwards compatible with the previous ones. They might be! But if they aren't, then you need to do maintenance if you want to use the modern development toolchain.
|
||||
A popular way to write JavaScript today is to compile it from TypeScript (which I will use frequently as an example, because TypeScript is probably the [best reason](https://en.wikipedia.org/wiki/Straw_man#Steelmanning) to use a build system). TypeScript does not run natively in web browsers, so TypeScript code is not protected by [ECMA's](https://developer.mozilla.org/en-US/docs/Glossary/ECMA) fanatical devotion to backwards compatibility. Like any dependency, new major TypeScript versions are not guaranteed to be backwards compatible with the previous ones. They might be! But if they aren't, then you need to do maintenance if you want to use the modern development toolchain.
|
||||
|
||||
Maintenance is a cost paid for with labor, and open-source codebases are the projects that can least afford to pay it. Opting not to use a build step drastically minimizes the labor required to keep htmx up-to-date. This experience has been borne out by [intercooler.js](https://intercoolerjs.org), the predecessor to htmx which is maintained indefinitely with (as I understand) very little effort. When htmx 1.0 was released, TypeScript was at version 4.1; when intercooler.js was released, TypeScript was pre-1.0. Would code written in those TypeScript versions compile unmodified in today's TypeScript compiler (version 5.1 at the time of writing)? Maybe, maybe not.
|
||||
|
||||
@ -60,6 +66,6 @@ This makes doing certain things with htmx very difficult. The [idiomorph algorit
|
||||
# Final Thoughts
|
||||
This essay might be better titled "Why htmx Doesn't Have a Build Step <em>Right Now</em>." As previously mentioned, circumstances change and these tradeoffs can be revisited at any time! One issue we're exploring at the moment has to do with releases. When htmx cuts releases, it uses a few different shell commands to populate the `dist` directory with minified and compressed versions of `htmx.js` (pedants are welcome to point out that this is obviously, in some sense, a build step). In the future, we might expand that script to auto-generate the [Universal Module Definition](https://github.com/umdjs/umd). Or we might have new distribution needs that require an even more involved setup. Who knows!
|
||||
|
||||
One of the core values of htmx is that it gives you *choice* in a web development ecosystem that has for the last decade been dominated by an increasingly complex JavaScript stack. Once you no longer have an enormous codebase of frontend JavaScript, there is far less pressure to adopt JavaScript on the backend. You can write backends in Python, Go, even NodeJS, and it doesn't matter to htmx—every mainstream language has mature solutions for formatting HTML. This is the principle of [Hypermedia On Whatever you'd Like (HOWL)](https://htmx.org/essays/hypermedia-on-whatever-youd-like/).
|
||||
One of the core values of htmx is that it gives you *choice* in a web development ecosystem that has for the last decade been dominated by an increasingly complex JavaScript stack. Once you no longer have an enormous codebase of frontend JavaScript, there is far less pressure to adopt JavaScript on the backend. You can write backends in Python, Go, even Node.js, and it doesn't matter to htmx—every mainstream language has mature solutions for formatting HTML. This is the principle of [Hypermedia On Whatever you'd Like (HOWL)](https://htmx.org/essays/hypermedia-on-whatever-youd-like/).
|
||||
|
||||
Writing JavaScript with no build process is one of the options available to you once you no longer require NextJS or SvelteKit to manage the spiraling complexity of SPA frameworks. That choice makes sense for htmx development today, and it may or may not make sense for your app too.
|
||||
Writing JavaScript with no build process is one of the options available to you once you no longer require Next.js or SvelteKit to manage the spiraling complexity of SPA frameworks. That choice makes sense for htmx development today, and it may or may not make sense for your app too.
|
||||
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "Prefer If Statements To Polymorphism..."
|
||||
description = """\
|
||||
In this collection of tweets, Carson Gross explores unconventional programming principles, including favoring if \
|
||||
statements over polymorphism, minimizing abstractions, and prioritizing practical, implementation-driven \
|
||||
development. He challenges traditional software design norms, advocating for simplicity, locality, and utility over \
|
||||
complexity and abstraction."""
|
||||
date = 2024-12-07
|
||||
updated = 2024-12-07
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -21,7 +26,7 @@ tag = ["posts"]
|
||||
> they should just do something useful man
|
||||
* *[The Minimize Abstractions Principle](https://x.com/htmx_org/status/1843806270559793475)*
|
||||
> The Minimize Abstractions Principle (MAP) is a computer programming principle that states that "a module should
|
||||
> minimize the number of abstractions it contains, both in API and in implementation. Stop navel gazing nerd.
|
||||
> minimize the number of abstractions it contains, both in API and in implementation. Stop navel gazing nerd."
|
||||
* *[The Try It Out Substitution Principle](https://x.com/htmx_org/status/1843807054970139139)*
|
||||
> The "Try It Out" Substitution Principle states that you should try something out and, if that doesn't work, think about why, and substitute something else for it instead.
|
||||
>
|
||||
@ -35,7 +40,7 @@ tag = ["posts"]
|
||||
>
|
||||
> when they exhaust that budget & ask for more, tell them they can have another abstraction when they remove an existing one
|
||||
>
|
||||
> when they complain, look for classes with the term "Factory", "Lookup" or "Visitor" in their names]
|
||||
> when they complain, look for classes with the term "Factory", "Lookup" or "Visitor" in their names
|
||||
* *[Fewer Functions](https://x.com/htmx_org/status/1843822378352291914)*
|
||||
> if your function is only called in one place, consider inlining it & reducing the total number of method signatures in your module, to help people better understand it
|
||||
>
|
||||
@ -43,7 +48,7 @@ tag = ["posts"]
|
||||
* *["God" Object](https://x.com/htmx_org/status/1843823231771521367)*
|
||||
> consider creating "God" objects that wrap a lot of functionality up in a single package
|
||||
>
|
||||
> consumers of your API don't want to learn 50 different classes to get something done, so give them a few that provide the core functionality of your module with minimal fuss]
|
||||
> consumers of your API don't want to learn 50 different classes to get something done, so give them a few that provide the core functionality of your module with minimal fuss
|
||||
* *[Copy & Paste Driven Development](https://x.com/htmx_org/status/1843827082687852706)*
|
||||
> copy&paste driven development is a development methodology where, when you need to reuse some code but in a slightly different manner, you copy & paste the code & then modify it to satisfy the new requirements
|
||||
>
|
||||
|
@ -1,9 +1,14 @@
|
||||
+++
|
||||
title = "REST Copypasta"
|
||||
description = """\
|
||||
These page provides some pre-written critiques of the common misuse of the term 'REST' in modern web development, \
|
||||
contrasting it with the true REST architecture defined by Roy Fielding. Copy these ready-made responses to helpfully \
|
||||
explain how JSON/RPC is often mislabeled as REST and highlight the role of hypermedia and API specifications in \
|
||||
defining REST-ful systems. You will surely not make any enemies or regret posting these responses in any way."""
|
||||
date = 2023-06-26
|
||||
updated = 2023-06-26
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
[extra]
|
||||
show_title = false
|
||||
|
@ -1,7 +1,15 @@
|
||||
+++
|
||||
title = "REST - Explained For Beginners"
|
||||
description = """\
|
||||
In this essay, Carson Gross presents the core concepts of REST (Representational State Transfer) explained in simple \
|
||||
terms for beginners. He breaks down Roy Fielding's dissertation to highlight key principles like the Uniform \
|
||||
Interface, Statelessness, Hypermedia (HATEOAS), and more, making REST easy to understand for non-academic web \
|
||||
developers."""
|
||||
date = 2021-07-13
|
||||
updated = 2022-02-06
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
There is no topic that generates more confusion in web development than the idea of Representational State Transfer,
|
||||
|
@ -1,9 +1,15 @@
|
||||
+++
|
||||
title = "The #ViewSource Affordance"
|
||||
description = """\
|
||||
In this essay, Carson Gross explores the significance of the #ViewSource affordance in preserving the open, \
|
||||
collaborative culture of the early web. He examines the impact of digital and technical enclosure on this culture, \
|
||||
highlights the importance of developer decisions in maintaining openness, and advocates for prioritizing \
|
||||
#ViewSource-friendly practices in modern web development."""
|
||||
date = 2023-09-21
|
||||
updated = 2023-09-21
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
> Not for nothing, Hypercard presaged the web's critical "#ViewSource" affordance, which allowed people to copy,
|
||||
@ -18,7 +24,7 @@ When people talk about open source software, that conversation is often dominate
|
||||
[the Free Software Foundation's notion of free software](https://www.gnu.org/philosophy/free-sw.html):
|
||||
|
||||
> “Free software” means software that respects users' freedom and community. Roughly, it means that the users have the
|
||||
> freedom to run, copy, distribute, study, change and improve the software."
|
||||
> freedom to run, copy, distribute, study, change and improve the software.
|
||||
|
||||
This definition of free software has been a useful one and, through advocating for it, the FSF has gifted the world a
|
||||
lot of wonderful open source software.
|
||||
@ -98,7 +104,7 @@ technical decisions and how those decisions can also contribute to the disappear
|
||||
|
||||
Inadvertently (for the most part) technical trends and decisions in web development in the last two decades have lead
|
||||
to what we term a "Technical Enclosure" of the web, a processes whereby technical decisions chip away at the #ViewSource
|
||||
affordance that Cory Doctrow discusses in the opening quote of this article, an affordance that existed as a commons
|
||||
affordance that Cory Doctorow discusses in the opening quote of this article, an affordance that existed as a commons
|
||||
for early web developers.
|
||||
|
||||
To see a stark example of the decline of the [#ViewSource](https://en.wikipedia.org/wiki/View-source_URI_scheme) affordance
|
||||
@ -122,8 +128,8 @@ challenging for even the most seasoned web developer.
|
||||
|
||||
A new web developer would have almost no chance of deriving any value from doing so.
|
||||
|
||||
Now, this is not to criticize the google engineer's technical decisions that lead to this situation _as technical
|
||||
decisions_: obviously, despite similar appearances, the google homepage of 2023 is far more sophisticated than the one
|
||||
Now, this is not to criticize the Google engineer's technical decisions that lead to this situation _as technical
|
||||
decisions_: obviously, despite similar appearances, the Google homepage of 2023 is far more sophisticated than the one
|
||||
available in 2000.
|
||||
|
||||
The 2023 google homepage is going to be a lot more complicated than the 2000 page and, given the zeitgeist, it is going to
|
||||
|
@ -1,9 +1,15 @@
|
||||
+++
|
||||
title = "SPA Alternative"
|
||||
description = """\
|
||||
In this essay, Carson Gross explores alternatives to Single Page Applications (SPAs), advocating for an \
|
||||
'HTML-Centric' development approach. He highlights the drawbacks of SPA complexity and the benefits of embracing \
|
||||
HTML as the core medium for web development, offering a simpler, more efficient way to build applications without \
|
||||
sacrificing interactivity. Carson discusses how tools like htmx enhance HTML's capabilities and encourages \
|
||||
developers to reconsider the dominant SPA paradigm with technical bravery."""
|
||||
date = 2020-10-29
|
||||
updated = 2022-02-06
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -74,7 +80,7 @@ This is in large part because HTML is a limited hypertext. In particular:
|
||||
* Only `<a>` and `<form>` can make HTTP requests
|
||||
* Only `click` & `submit` events can trigger them
|
||||
* Only GET & POST [HTTP Methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) are widely available
|
||||
* A request must replace the entire screen, leading to a clunkly and sometimes jarring user experience
|
||||
* A request must replace the entire screen, leading to a clunky and sometimes jarring user experience
|
||||
|
||||
Of course, none of the constraints are inherent in the concept of a hypertext, and the goal of [htmx](@/_index.md)
|
||||
is to remove each of them.
|
||||
|
@ -1,9 +1,13 @@
|
||||
+++
|
||||
title = "Splitting Your Data & Application APIs: Going Further"
|
||||
description = """\
|
||||
If you split your API into Data and Application APIs, you should consider changing your Application API from JSON to \
|
||||
Hypermedia (HTML) and using a hypermedia-oriented library like htmx to reap the benefits of the hypermedia model \
|
||||
(simplicity, reliability, and flexibility)."""
|
||||
date = 2021-09-16
|
||||
updated = 2022-02-06
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -1,9 +1,16 @@
|
||||
+++
|
||||
title = "Template Fragments"
|
||||
description = """\
|
||||
In this essay, Carson Gross explores the concept of template fragments, a feature in server-side rendering (SSR) \
|
||||
that allows partial rendering of content within templates. He highlights the benefits of using template fragments in \
|
||||
hypermedia-driven applications, providing a cleaner and more maintainable approach compared to traditional template \
|
||||
decomposition. Carson showcases the use of template fragments in the Chill templating language and discusses how \
|
||||
this feature improves the developer experience when working with htmx and other hypermedia-oriented libraries. He \
|
||||
also includes examples and known implementations of template fragments in various programming languages."""
|
||||
date = 2022-08-03
|
||||
updated = 2023-03-18
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -153,9 +160,12 @@ Here are some known implementations of the fragment concept:
|
||||
* [chameleon_partials package](https://github.com/mikeckennedy/chameleon_partials)
|
||||
* [htmlgenerator](https://github.com/basxsoftwareassociation/htmlgenerator)
|
||||
* [django-template-partials](https://pypi.org/project/django-template-partials/) ([repository](https://github.com/carltongibson/django-template-partials))
|
||||
* [django-block-fragments](https://github.com/medihack/django-block-fragments)
|
||||
* .NET
|
||||
* [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)
|
||||
* Raku
|
||||
* [Cro Templates](https://github.com/croservices/cro-website/blob/main/docs/reference/cro-webapp-template-syntax.md#fragments)
|
||||
|
||||
Please [let me know](/discord) if you know of others, so I can add them to this list.
|
||||
|
@ -1,9 +1,16 @@
|
||||
+++
|
||||
title = "Two Approaches To Decoupling"
|
||||
description = """\
|
||||
Carson Gross explores two different approaches to decoupling in web applications: decoupling at the application \
|
||||
level using a JSON Data API and decoupling at the network architecture level using a hypermedia API. He discusses \
|
||||
the trade-offs between the two methods, highlighting how a hypermedia API, despite introducing tighter coupling at \
|
||||
the application level, offers greater resilience to change at the system level. Carson also touches on the \
|
||||
limitations of each approach and discusses strategies like GraphQL and splitting APIs to address specific challenges \
|
||||
in web development."""
|
||||
date = 2022-05-01
|
||||
updated = 2022-05-01
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -157,7 +164,7 @@ is to say, at the system level. [Hypermedia systems](https://hypermedia.systems
|
||||
client (in the case of the web, the browser) from the hypermedia server.
|
||||
|
||||
This is accomplished primarily via the Uniform Interface constraint of REST and, in particular, by using
|
||||
Hypermedia As The Engine of Application State ([HATOEAS](/essays/hateoas)).
|
||||
Hypermedia As The Engine of Application State ([HATEOAS](/essays/hateoas)).
|
||||
|
||||
This style of decoupling allows tighter coupling at the higher application level (which we have seen may be an
|
||||
_inherent_ coupling) while still retaining the benefits of decoupling for the overall system.
|
||||
|
219
www/content/essays/vendoring.md
Normal file
219
www/content/essays/vendoring.md
Normal file
@ -0,0 +1,219 @@
|
||||
+++
|
||||
title = "Vendoring"
|
||||
description = """\
|
||||
Carson Gross explores the concept of 'vendoring' in software development, where external project sources are copied \
|
||||
directly into a project. He covers the benefits of vendoring, such as improved visibility and control over \
|
||||
dependencies, and discusses challenges like transitive dependencies and the culture of dependency in modern software \
|
||||
development. He also contrasts vendoring with modern dependency management tools, and considers the potential for \
|
||||
vendor-first dependency managers to combine the strengths of both approaches. He encourages a rethinking of \
|
||||
dependencies and promotes a more independent approach to software development."""
|
||||
date = 2025-01-27
|
||||
updated = 2025-01-27
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
"Vendoring" software is a technique where you copy the source of another project directly into your own project.
|
||||
|
||||
It is an old technique that has been used for time immemorial in software development, but the term "vendoring" to
|
||||
describe it appears to have originated in the [ruby community](https://stackoverflow.com/posts/72115282/revisions).
|
||||
|
||||
Vendoring can be and is still used today. You can vendor htmx, for example, quite easily.
|
||||
|
||||
Assuming you have a `/js/vendor` directory in your project, you can just download the source into your own project like
|
||||
so:
|
||||
|
||||
```bash
|
||||
curl https://raw.githubusercontent.com/bigskysoftware/htmx/refs/tags/v2.0.4/dist/htmx.min.js > /js/vendor/htmx-2.0.4.min.js
|
||||
```
|
||||
|
||||
You then include the library in your `head` tag:
|
||||
|
||||
```html
|
||||
<script src="/js/vendor/htmx-2.0.4.min.js"></script>
|
||||
```
|
||||
|
||||
And then you check the htmx source into your own source control repository. (I would even recommend considering using
|
||||
the [non-minimized version](https://raw.githubusercontent.com/bigskysoftware/htmx/refs/tags/v2.0.4/dist/htmx.js), so
|
||||
you can better understand and debug the code.)
|
||||
|
||||
That's it, that's vendoring.
|
||||
|
||||
## Vendoring Strengths
|
||||
|
||||
OK, great, so what are some strengths of vendoring libraries like this?
|
||||
|
||||
It turns out there are quite a few:
|
||||
|
||||
* Your entire project is checked in to your source repository, so no external systems beyond your source control need
|
||||
to be involved when building it
|
||||
* Vendoring dramatically improves dependency *visibility*: you can _see_ all the code your project depends on, so you
|
||||
won't have a situation like we have in htmx, where we feel like we only have a few development dependencies, whe in
|
||||
fact we may have a lot
|
||||
* This also means if you have a good debugger, you can step into the library code as easily as any other code. You
|
||||
can also read it, learn from it and even modify it if necessary.
|
||||
* From a security perspective, you aren't relying on opaque code. Even if your package manager has
|
||||
an integrity hash system, the actual code may be opaque to you. With vendored code it is checked in and can be
|
||||
analysed automatically or by a security team.
|
||||
* Personally, it has always seemed crazy to me that people will often resolve dependencies at deployment time, right
|
||||
when your software is about to go out the door. If that bothers you, like it does me, vendoring puts a stop to it.
|
||||
|
||||
On the other hand, vendoring also has one massive drawback: there typically isn't a good way to deal with what is called
|
||||
the [transitive dependency](https://en.wikipedia.org/wiki/Transitive_closure) problem.
|
||||
|
||||
If htmx had sub-dependencies, that is, other libraries that it depended on, then to vendor it properly you would have to
|
||||
start vendoring all those libraries as well. And if those dependencies had further dependencies, you'd need to install
|
||||
them as well... And on and on.
|
||||
|
||||
Worse, two dependencies might depend on the same library, and you'll need to make sure you get the
|
||||
[correct version](https://en.wikipedia.org/wiki/Dependency_hell) of that library for everything to work.
|
||||
|
||||
This can get pretty difficult to deal with, but I want to make a paradoxical claim that this weakness (and, again, it's
|
||||
a real one) is actually a strength in some way:
|
||||
|
||||
Because dealing with large numbers of dependencies is difficult, vendoring encourages a culture of _independence_.
|
||||
|
||||
You get more of what you make easy, and if you make dependencies easy, you get more of them. Making dependencies,
|
||||
_especially_ transitive dependencies, more difficult would make them less common.
|
||||
|
||||
And, as we will see in a bit, maybe fewer dependencies isn't such a bad thing.
|
||||
|
||||
## Dependency Managers
|
||||
|
||||
That's great and all, but there are [significant](https://gist.github.com/datagrok/8577287)
|
||||
[drawbacks](https://web.archive.org/web/20180216205752/http://blog.bithound.io/why-we-stopped-vendoring-our-npm-dependencies/)
|
||||
to vendoring, particular the transitive dependency problem.
|
||||
|
||||
Modern software engineering uses dependency managers to deal with the dependencies of software projects. These tools
|
||||
allow you to specify your projects dependencies, typically via some sort of file. They then they will install those
|
||||
dependencies and resolve and manage all the other dependencies that are necessary for those dependencies to work.
|
||||
|
||||
One of the most widely used package managers is NPM: The [Node Package Manager](https://www.npmjs.com/). Despite having
|
||||
no runtime dependencies, htmx uses NPM to specify 16 development dependencies. Development dependencies are dependencies
|
||||
that are necessary for development of htmx, but not for running it. You can see the dependencies at the bottom of
|
||||
the NPM [`package.json`](https://github.com/bigskysoftware/htmx/blob/master/package.json) file for the project.
|
||||
|
||||
Dependency managers are a crucial part of modern software development and many developers today couldn't imagine
|
||||
writing software without them.
|
||||
|
||||
### The Trouble with Dependency Managers
|
||||
|
||||
So dependency managers solve the transitive dependency problem that vendoring has. But, as with everything in software
|
||||
engineering, there are tradeoffs associated with them. To see some of these tradeoffs, let's take a look at the
|
||||
[`package-lock.json`](https://github.com/bigskysoftware/htmx/blob/master/package-lock.json) file in htmx.
|
||||
|
||||
NPM generates a `package-lock.json` file that contains the resolved transitive closure of dependencies for a project, with
|
||||
the concrete versions of those dependencies. This helps ensure that the same dependencies are used unless a user
|
||||
explicitly updates them.
|
||||
|
||||
If you take a look at the `package-lock.json` for htmx, you will find that the original 13 development dependencies have
|
||||
ballooned into a total of 411 dependencies when all is said and done.
|
||||
|
||||
htmx, it turns out, relies on a huge number of packages, despite priding itself on being a relatively lean. In fact,
|
||||
the `node_modules` folder in htmx is a whopping 110 megabytes!
|
||||
|
||||
But, beyond this bloat there are deeper problems lurking in that mass of dependencies.
|
||||
|
||||
While writing this essay I found that htmx apparently depends on the
|
||||
[`array.prototype.findlastindex`](https://www.npmjs.com/package/array.prototype.findlastindex), a
|
||||
[polyfill](https://en.wikipedia.org/wiki/Polyfill_(programming)) for a JavaScript feature introduced in
|
||||
[2022](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex).
|
||||
|
||||
Now, [htmx 1.x](https://v1.htmx.org/) is IE compatible, and I don't *want* polyfills for _anything_: I want to write
|
||||
code that will work in IE without any additional library support. And yet a polyfill has snuck in via a chain
|
||||
of dependencies (htmx does not directly rely on it) that introduces a dangerous polyfill that would let me write
|
||||
code that would break in IE, as well as other older browsers.
|
||||
|
||||
This polyfill may or may not be available when I run the htmx [test suite](https://htmx.org/test/) (it's hard to tell)
|
||||
but that's the point: some dangerous code has snuck into my project without me even knowing it, due to the number
|
||||
and complexity of the (development) dependencies it has.
|
||||
|
||||
This demonstrates significant _cultural_ problem with dependency managers:
|
||||
|
||||
They tend to foster a culture of, well, dependency.
|
||||
|
||||
A spectacular example of this was the infamous [left-pad incident](https://en.wikipedia.org/wiki/Npm_left-pad_incident),
|
||||
in which an engineer took down a widely used package and broke the build at companies like Facebook, PayPal, Netflix,
|
||||
etc.
|
||||
|
||||
That was a relatively innocuous, although splashy, issue, but a more serious concern is
|
||||
[supply chain attacks](https://en.wikipedia.org/wiki/Supply_chain_attack), where a hostile entity is able to compromise
|
||||
a company via code injected unwittingly via dependencies.
|
||||
|
||||
The larger our dependency graph gets, the worse these problems get.
|
||||
|
||||
## Dependencies Reconsidered
|
||||
|
||||
I'm not the only person thinking about our culture of dependency. Here's what some other, smarter folks have to say
|
||||
about it:
|
||||
|
||||
[Armin Ronacher](https://x.com/mitsuhiko), creator of [flask](https://flask.palletsprojects.com/en/stable/)
|
||||
recently said this on [the ol'twits](https://x.com/mitsuhiko/status/1882739157120041156):
|
||||
|
||||
> The more I build software, the more I despise dependencies. I greatly prefer people copy/pasting stuff into their own
|
||||
> code bases or re-implement it. Unfortunately the vibe of the time does not embrace that idea much. I need that vibe
|
||||
> shift.
|
||||
|
||||
He also wrote a great blog post about his
|
||||
[experience with package management](https://lucumr.pocoo.org/2025/1/24/build-it-yourself/) in the Rust ecosystem:
|
||||
|
||||
> It's time to have a new perspective: we should give kudos to engineers who write a small function themselves instead
|
||||
> of hooking in a transitive web of crates. We should be suspicious of big crate graphs. Celebrated are the minimal
|
||||
> dependencies, the humble function that just quietly does the job, the code that doesn't need to be touched for years
|
||||
> because it was done right once.
|
||||
|
||||
Please go read it in full.
|
||||
|
||||
Back in 2021, [Tom Macwright](https://macwright.com) wrote this in
|
||||
[Vendor by default](https://macwright.com/2021/03/11/vendor-by-default)
|
||||
|
||||
> But one thing that I do think is sort of unusual is: I’m vendoring a lot of stuff.
|
||||
>
|
||||
> Vendoring, in the programming sense, means “copying the source code of another project into your project.” It’s in
|
||||
> contrast to the practice of using dependencies, which would be adding another project’s name to your package.json
|
||||
> file and having npm or yarn download and link it up for you.
|
||||
|
||||
I highly recommend reading his take on vendoring as well.
|
||||
|
||||
## Software Designed To Be Vendored
|
||||
|
||||
Some good news, if you are an open source developer and like the idea of vendoring, is that there is a simple way to
|
||||
make your software vendor-friendly: remove as many dependencies as you can.
|
||||
|
||||
[DaisyUI](https://daisyui.com/), for example, has been in the process of
|
||||
[removing their dependencies](https://x.com/Saadeghi/status/1882556881253826941), going from 100 dependencies in
|
||||
version 3 to 0 in version 5.
|
||||
|
||||
There is also a set htmx-adjacent projects that are taking vendoring seriously:
|
||||
|
||||
* [Surreal](https://github.com/gnat/surreal) - a lightweight jQuery alternative
|
||||
* [Facet](https://github.com/kgscialdone/facet) - an HTML-oriented Web Component library
|
||||
* [fixi](https://github.com/bigskysoftware/fixi) - a minimal htmx alternative
|
||||
|
||||
None of these JavaScript projects are available in NPM, and all of them [recommend](https://github.com/gnat/surreal#-install)
|
||||
[vendoring](https://github.com/kgscialdone/facet#installation) the [software](https://github.com/bigskysoftware/fixi#installing)
|
||||
into your own project as the primary installation mechanism.
|
||||
|
||||
## Vendor First Dependency Managers?
|
||||
|
||||
The last thing I want to briefly mention is a technology that combines both vendoring and dependency management:
|
||||
vendor-first dependency managers. I have never worked with one before, but I have been pointed to
|
||||
[vend](https://github.com/fosskers/vend), a common lisp vendor oriented package manager (with a great README), as well
|
||||
as [go's vendoring option](https://go.dev/ref/mod#vendoring).
|
||||
|
||||
In writing this essay, I also came across [vendorpull](https://github.com/sourcemeta/vendorpull) and
|
||||
[git-vendor](https://github.com/brettlangdon/git-vendor), both of which are small but interesting projects.
|
||||
|
||||
These all look like excellent tools, and it seems to me that there is an opportunity for some of them (and tools like
|
||||
them) to add additional functionality to address the traditional weaknesses of vendoring, for example:
|
||||
|
||||
* Managing transitive dependencies, if any
|
||||
* Relatively easy updates of those dependencies
|
||||
* Managing local modifications made to dependencies (and maybe help manage contributing them upstream?)
|
||||
|
||||
With these additional features I wonder if vendor-first dependency managers could compete with "normal" dependency
|
||||
managers in modern software development, perhaps combining some of the benefits of both approaches.
|
||||
|
||||
Regardless, I hope that this essay has helped you think a bit more about dependencies and perhaps planted the idea that
|
||||
maybe your software could be a little less, well, dependent on dependencies.
|
@ -1,9 +1,16 @@
|
||||
+++
|
||||
template = "demo.html"
|
||||
title = "View Transitions"
|
||||
description = """\
|
||||
Carson Gross explores the evolution of web applications and the significance of view transitions in improving user \
|
||||
experience. He discusses the limitations of traditional web design, where full-page refreshes create an unpleasant \
|
||||
experience, and how modern technologies like CSS transitions and the View Transition API aim to enhance aesthetic \
|
||||
smoothness. Carson explains how htmx leverages the View Transition API to bring seamless transitions to \
|
||||
hypermedia-driven applications, offering an alternative to single-page applications (SPAs) and highlighting its \
|
||||
potential once widely available in HTML."""
|
||||
date = 2023-04-11
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -18,7 +25,7 @@ the application, even if it has feature-parity with an SPA version:
|
||||
> delete a contact. This is because every user interaction (link click or form submission) requires a full page
|
||||
> refresh, with a whole new HTML document to process after each action.
|
||||
>
|
||||
> *–Hypermedia Systems - [Chapter 5](https://hypermedia.systems/book/extending-html-as-hypermedia/)*
|
||||
> *–Hypermedia Systems - [Chapter 4](https://hypermedia.systems/extending-html-as-hypermedia/)*
|
||||
|
||||
This jarring "ka-chunk" between webpages, often with a [Flash of Unstyled Content](https://webkit.org/blog/66/the-fouc-problem/)
|
||||
has been with us forever and, while modern browsers have improved the situation somewhat (while, unfortunately, also making
|
||||
|
@ -1,8 +1,15 @@
|
||||
+++
|
||||
title = "Web Security Basics (with htmx)"
|
||||
description = """\
|
||||
This guide by Alexander Petros provides essential web security best practices for building applications with htmx, \
|
||||
focusing on safe handling of dynamic, user-generated content. It covers fundamental principles such as using only \
|
||||
trusted routes, employing auto-escaping template engines, and securing cookies to prevent common vulnerabilities \
|
||||
like Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF). Aimed at developers familiar with backend \
|
||||
server construction, it emphasizes security techniques that are easy to implement and crucial for protecting user \
|
||||
data in dynamic web applications."""
|
||||
date = 2024-02-06
|
||||
authors = ["Alexander Petros"]
|
||||
[taxonomies]
|
||||
author = ["Alexander Petros"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -53,7 +60,7 @@ The reason for this is simple: htmx inserts the response from that route directl
|
||||
|
||||
Fortunately, this is a very easy rule to follow. Hypermedia APIs (i.e. HTML) are [specific to the layout of your application](https://htmx.org/essays/hypermedia-apis-vs-data-apis/), so there is almost never any reason you'd *want* to insert someone else's HTML into your page. All you have to do is make sure you only call your own routes (htmx 2 will actually disable calling other domains by default).
|
||||
|
||||
Though it's not quite as popular these days, a common SPA pattern was to separate the frontend and backend into different repositories, and sometimes even to serve them from different URLs. This would require using absolute URLs in the frontend, and often, [disabling CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). With htmx (and, to be fair, modern React with NextJS) this is an anti-pattern.
|
||||
Though it's not quite as popular these days, a common SPA pattern was to separate the frontend and backend into different repositories, and sometimes even to serve them from different URLs. This would require using absolute URLs in the frontend, and often, [disabling CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). With htmx (and, to be fair, modern React with Next.js) this is an anti-pattern.
|
||||
|
||||
Instead, you simply serve your HTML frontend from the same server (or at least the same domain) as your backend, and everything else falls into place: you can use relative URLs, you'll never have trouble with CORS, and you'll never call anyone else's backend.
|
||||
|
||||
|
@ -1,8 +1,15 @@
|
||||
+++
|
||||
title = "Web Components Work Great with htmx"
|
||||
description = """\
|
||||
This essay by Alexander Petros explores how Web Components can be integrated seamlessly with htmx, a library that \
|
||||
enables dynamic web pages through HTML. It discusses the flexibility of htmx in handling interactive elements like \
|
||||
Web Components alongside traditional server-driven approaches, such as multi-page apps. By using the example of an \
|
||||
editable carnival ride table, Alexander demonstrates how Web Components simplify functionality without the need for \
|
||||
heavy JavaScript frameworks, highlighting their compatibility with htmx's DOM-based lifecycle. Alexander also \
|
||||
addresses potential challenges and how htmx manages them efficiently."""
|
||||
date = 2024-11-13
|
||||
authors = ["Alexander Petros"]
|
||||
[taxonomies]
|
||||
author = ["Alexander Petros"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -145,7 +152,7 @@ A lot of the problems that JavaScript frameworks have supporting Web Components
|
||||
|
||||
Web Components [have DOM-based lifecycles](https://dev.to/ryansolid/web-components-are-not-the-future-48bh), so they are difficult for JavaScript frameworks, which often manipulate elements outside of the DOM, to work with.
|
||||
Frameworks have to account for some [bizarre and arguably buggy](https://x.com/Rich_Harris/status/1841467510194843982) APIs that behave differently for native DOM elements than they do for custom ones.
|
||||
Here at htmx, we agree with with [SvelteJS creator Rich Harris](https://x.com/Rich_Harris/status/1839484645194277111): "web components are [not] useful primitives on which to build web frameworks."
|
||||
Here at htmx, we agree with [SvelteJS creator Rich Harris](https://x.com/Rich_Harris/status/1839484645194277111): "web components are [not] useful primitives on which to build web frameworks."
|
||||
|
||||
The good news is that htmx [is not really a JavaScript web framework](@/essays/is-htmx-another-javascript-framework.md).
|
||||
The DOM-based lifecycles of custom elements work great in htmx, because everything in htmx has a DOM-based lifecycle—we get stuff from the server, and we add it to the DOM.
|
||||
|
@ -1,9 +1,17 @@
|
||||
+++
|
||||
title = "When Should You Use Hypermedia?"
|
||||
description = """\
|
||||
This essay by Carson Gross explores when to use hypermedia in web development, highlighting its advantages and \
|
||||
trade-offs. Carson discusses scenarios where hypermedia is a good fit, such as text and image-heavy UIs, CRUD \
|
||||
applications, and nested UIs with well-defined areas. He also addresses when hypermedia may not be ideal, including \
|
||||
situations with frequent UI state updates, offline requirements, or dynamic interdependencies. Using a \
|
||||
'Transitional' approach (as suggested by Rich Harris), Carson advocates for combining hypermedia with other \
|
||||
strategies to optimize web application development, emphasizing its practical benefits and alignment with the web's \
|
||||
architecture."""
|
||||
date = 2022-10-23
|
||||
updated = 2023-02-03
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -174,6 +182,15 @@ architecture, than vice-versa. Isolated client-side components can communicate
|
||||
via [events](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events), in the manner demonstrated
|
||||
in the [drag-and-drop Sortable.js + htmx](@/examples/sortable.md) example.
|
||||
|
||||
### _...If you want integrated copy & paste components_
|
||||
|
||||
Recently we have seen the rise of "Copy & Paste" friendly components such as [ShadCN](https://ui.shadcn.com/).
|
||||
These components are typically designed for a specific front end framework such as React, and choosing htmx means that
|
||||
you can't use them.
|
||||
|
||||
There are front-end library neutral component libraries such as [lit](https://lit.dev/), but they are not as integrated
|
||||
with htmx as ShadCN is with React.
|
||||
|
||||
### _...If your team is not on board_
|
||||
|
||||
A final reason to not choose hypermedia isn't technical, but rather sociological: currently, hypermedia simply isn't
|
||||
|
@ -1,9 +1,16 @@
|
||||
+++
|
||||
title = "Why Gumroad Didn't Choose htmx"
|
||||
description = """\
|
||||
In this essay, Sahil Lavingia, CEO of Gumroad, explains why the company decided against using htmx for its new \
|
||||
project, Helper, in favor of React and Next.js. He shares the challenges faced with htmx, including issues with \
|
||||
developer experience, user experience limitations, scalability, and AI tool support. While acknowledging htmx's \
|
||||
potential for simpler projects, Lavingia emphasizes how React and Next.js offered better solutions for complex \
|
||||
features like real-time collaboration, drag-and-drop functionality, and dynamic forms. Ultimately, Lavingia \
|
||||
highlights the importance of selecting technologies that can grow with the project's needs."""
|
||||
date = 2024-09-30
|
||||
updated = 2024-09-30
|
||||
authors = ["Sahil Lavingia"]
|
||||
[taxonomies]
|
||||
author = ["Sahil Lavingia"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -66,7 +73,7 @@ Here's why:
|
||||
<a href="/img/gumroad-green.jpeg" target="_blank">
|
||||
<img alt="Gumroad Green" src="/img/gumroad-green.jpeg" style="width: 100%">
|
||||
</a>
|
||||
<figcaption>Source with NextJS - Click Image To View</figcaption>
|
||||
<figcaption>Source with Next.js - Click Image To View</figcaption>
|
||||
</figure>
|
||||
|
||||
Ultimately, we ended up moving to React/Next.js, which has been a really great fit for building the complex UX we've
|
||||
|
@ -1,9 +1,16 @@
|
||||
+++
|
||||
title = "Why I Tend Not To Use Content Negotiation"
|
||||
description = """\
|
||||
In this essay, Carson Gross explores his preference for separating JSON and hypermedia APIs instead of using content \
|
||||
negotiation, a feature in HTTP that allows clients to request different formats (e.g., HTML, JSON). He discusses the \
|
||||
limitations of content negotiation in APIs, especially when mixing stable, versioned JSON data APIs with dynamic, \
|
||||
UI-driven hypermedia APIs. Carson argues that by splitting these concerns into distinct APIs, developers can better \
|
||||
maintain stability for data APIs while allowing flexibility for hypermedia APIs to evolve with user interface needs. \
|
||||
He also highlights the challenges content negotiation introduces to API design and scalability."""
|
||||
date = 2023-11-18
|
||||
updated = 2023-11-18
|
||||
authors = ["Carson Gross"]
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -154,7 +161,7 @@ is making a SoC argument.)
|
||||
## So What's The Alternative?
|
||||
|
||||
The alternative is, as I advocate in [Splitting Your APIs](@/essays/splitting-your-apis.md), er, well, splitting your
|
||||
APIs. This means providing different paths (or sub-domains, or whatever) for your JSON API and your hypermedia (HTML)
|
||||
APIs. This means providing different paths (or subdomains, or whatever) for your JSON API and your hypermedia (HTML)
|
||||
API.
|
||||
|
||||
Going back to our contacts API, we might have the following:
|
||||
|
@ -1,9 +1,16 @@
|
||||
+++
|
||||
title = "You Can't Build Interactive Web Apps Except as Single Page Applications... And Other Myths"
|
||||
description = """\
|
||||
Tony Alaribe challenges common myths about multi-page applications (MPAs) and explores how modern browser \
|
||||
technologies can enable fast, interactive, and offline-capable web applications without relying on single-page \
|
||||
application (SPA) frameworks. Alaribe discusses advancements in service workers, caching, and cross-document \
|
||||
transitions, offering insights into building efficient MPAs. By debunking myths like slow page transitions and the \
|
||||
necessity of JavaScript-heavy frameworks, Alaribe highlights how developers can leverage HTML, CSS, and minimal \
|
||||
JavaScript to create robust, user-friendly web apps in 2024."""
|
||||
date = 2024-09-20
|
||||
updated = 2024-09-20
|
||||
authors = ["Tony Alaribe"]
|
||||
[taxonomies]
|
||||
author = ["Tony Alaribe"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -144,9 +151,9 @@ help you—I prefer using Google's [Workbox](https://developer.chrome.com/docs/w
|
||||
By following these steps, you instruct the browser to serve cached assets whenever possible, drastically reducing load
|
||||
times and improving the overall performance of your multi-page application.
|
||||
|
||||

|
||||

|
||||
|
||||
Image showing the registered service worker from the chrome browser console.
|
||||
Image showing the registered service worker from the Chrome browser console.
|
||||
|
||||
### [`Speculation Rules API`](https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API): Prerender pages for instant page navigation.
|
||||
|
||||
@ -235,7 +242,7 @@ don't have to do everything server-side. Many HTMX and regular MPA users continu
|
||||
Hyperscript where appropriate.
|
||||
|
||||
In situations where robust interactivity is helpful, you can lean into the component islands architecture using
|
||||
WebComponents or any javascript framework (react, angular, etc) of your choice. That way, instead of your entire
|
||||
WebComponents or any javascript framework (React, Angular, etc.) of your choice. That way, instead of your entire
|
||||
application being an SPA, you can leverage those frameworks specifically for the bits of your application that need that
|
||||
interactivity.
|
||||
|
||||
|
@ -126,10 +126,12 @@ This event is triggered right before a request is sent. You may not cancel the
|
||||
|
||||
### Event - `htmx:beforeSwap` {#htmx:beforeSwap}
|
||||
|
||||
This event is triggered before any new content has been [swapped into the DOM](@/docs.md#swapping). If you call `preventDefault()` on the event to cancel it, no swap will occur.
|
||||
This event is triggered before any new content has been [swapped into the DOM](@/docs.md#swapping).
|
||||
Most values on `detail` can be set to override subsequent behavior, other than where response headers take precedence.
|
||||
If you call `preventDefault()` on the event to cancel it, no swap will occur.
|
||||
|
||||
You can modify the default swap behavior by modifying the `shouldSwap` and `target` properties of the event detail. See
|
||||
the documentation on [configuring swapping](@/docs.md#modifying_swapping_behavior_with_events) for more details.
|
||||
You can modify the default swap behavior by modifying the `shouldSwap`, `selectOverride`, `swapOverride` and `target` properties of the event detail.
|
||||
See the documentation on [configuring swapping](@/docs.md#modifying_swapping_behavior_with_events) for more details.
|
||||
|
||||
##### Details
|
||||
|
||||
@ -139,6 +141,10 @@ the documentation on [configuring swapping](@/docs.md#modifying_swapping_behavio
|
||||
* `detail.requestConfig.elt` - the element that dispatched the request
|
||||
* `detail.shouldSwap` - if the content will be swapped (defaults to `false` for non-200 response codes)
|
||||
* `detail.ignoreTitle` - if `true` any title tag in the response will be ignored
|
||||
* `detail.isError` - whether error events should be triggered and also determines the values of `detail.successful` and `detail.failed` in later events
|
||||
* `detail.serverResponse` - the server response as text to be used for the swap
|
||||
* `detail.selectOverride` - add this to use instead of an [`hx-select`](@/attributes/hx-select.md) value
|
||||
* `detail.swapOverride` - add this to use instead of an [`hx-swap`](@/attributes/hx-swap.md) value
|
||||
* `detail.target` - the target of the swap
|
||||
|
||||
### Event - `htmx:beforeTransition` {#htmx:beforeTransition}
|
||||
@ -299,10 +305,6 @@ This event is triggered before the content is saved in the history api.
|
||||
* `detail.path` - the path and query of the page being restored
|
||||
* `detail.historyElt` - the history element being restored into
|
||||
|
||||
##### Details
|
||||
|
||||
* `detail.config` - the config that will be passed to the `EventSource` constructor
|
||||
|
||||
### Event - `htmx:load` {#htmx:load}
|
||||
|
||||
This event is triggered when a new node is loaded into the DOM by htmx.
|
||||
|
@ -1,6 +1,6 @@
|
||||
+++
|
||||
title = "Examples"
|
||||
insert_anchor_links = "left"
|
||||
insert_anchor_links = "heading"
|
||||
+++
|
||||
|
||||
## Server-side Integration Examples
|
||||
@ -30,6 +30,7 @@ You can copy and paste them and then adjust them for your needs.
|
||||
| [Animations](@/examples/animations.md) | Demonstrates various animation techniques |
|
||||
| [File Upload](@/examples/file-upload.md) | Demonstrates how to upload a file via ajax with a progress bar |
|
||||
| [Preserving File Inputs after Form Errors](@/examples/file-upload-input.md) | Demonstrates how to preserve file inputs after form errors |
|
||||
| [Reset User Input](@/examples/reset-user-input.md) | Demonstrates how to reset form inputs after submission |
|
||||
| [Dialogs - Browser](@/examples/dialogs.md) | Demonstrates the prompt and confirm dialogs |
|
||||
| [Dialogs - UIKit](@/examples/modal-uikit.md) | Demonstrates modal dialogs using UIKit |
|
||||
| [Dialogs - Bootstrap](@/examples/modal-bootstrap.md) | Demonstrates modal dialogs using Bootstrap |
|
||||
|
@ -8,17 +8,17 @@ This example actively searches a contacts database as the user enters text.
|
||||
We start with a search input and an empty table:
|
||||
|
||||
```html
|
||||
<h3>
|
||||
Search Contacts
|
||||
<span class="htmx-indicator">
|
||||
<img src="/img/bars.svg"/> Searching...
|
||||
</span>
|
||||
<h3>
|
||||
Search Contacts
|
||||
<span class="htmx-indicator">
|
||||
<img src="/img/bars.svg"/> Searching...
|
||||
</span>
|
||||
</h3>
|
||||
<input class="form-control" type="search"
|
||||
name="search" placeholder="Begin Typing To Search Users..."
|
||||
hx-post="/search"
|
||||
hx-trigger="input changed delay:500ms, search"
|
||||
hx-target="#search-results"
|
||||
<input class="form-control" type="search"
|
||||
name="search" placeholder="Begin Typing To Search Users..."
|
||||
hx-post="/search"
|
||||
hx-trigger="input changed delay:500ms, keyup[key=='Enter'], load"
|
||||
hx-target="#search-results"
|
||||
hx-indicator=".htmx-indicator">
|
||||
|
||||
<table class="table">
|
||||
@ -34,18 +34,17 @@ We start with a search input and an empty table:
|
||||
</table>
|
||||
```
|
||||
|
||||
The input issues a `POST` to `/search` on the [`input`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) event and sets the body of the table to be the resulting content. Note that the `keyup` event could be used as well, but would not fire if the user pasted text with their mouse (or any other non-keyboard method).
|
||||
The input issues a `POST` to `/search` on the [`input`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) event and sets the body of the table to be the resulting content.
|
||||
|
||||
We add the `delay:500ms` modifier to the trigger to delay sending the query until the user stops typing. Additionally,
|
||||
we add the `changed` modifier to the trigger to ensure we don't send new queries when the user doesn't change the
|
||||
value of the input (e.g. they hit an arrow key, or pasted the same value).
|
||||
value of the input (e.g. they hit an arrow key, or pasted the same value).
|
||||
|
||||
Since we use a `search` type input we will get an `x` in the input field to clear the input.
|
||||
To make this trigger a new `POST` we have to specify another trigger. We specify another trigger by using a comma to
|
||||
separate them. The `search` trigger will be run when the field is cleared but it also makes it possible to override
|
||||
the 500 ms `input` event delay by just pressing enter.
|
||||
We can use multiple triggers by separating them with a comma, this way we add 2 more triggers:
|
||||
- `keyup[key=='Enter']` triggers once enter is pressed. We use [event filters](/attributes/hx-trigger#standard-event-filters) here to check for [the key property in the KeyboardEvent object](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key).
|
||||
- `load` in order to show all results initially on load.
|
||||
|
||||
Finally, we show an indicator when the search is in flight with the `hx-indicator` attribute.
|
||||
Finally, we show an indicator when the search is in flight with the `hx-indicator` attribute.
|
||||
|
||||
{{ demoenv() }}
|
||||
|
||||
@ -75,11 +74,11 @@ Search Contacts
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
<input class="form-control" type="search"
|
||||
name="search" placeholder="Begin Typing To Search Users..."
|
||||
hx-post="/search"
|
||||
hx-trigger="input changed delay:500ms, search"
|
||||
hx-target="#search-results"
|
||||
<input class="form-control" type="search"
|
||||
name="search" placeholder="Begin Typing To Search Users..."
|
||||
hx-post="/search"
|
||||
hx-trigger="input changed delay:500ms, keyup[key=='Enter'], load"
|
||||
hx-target="#search-results"
|
||||
hx-indicator=".htmx-indicator">
|
||||
|
||||
<table class="table">
|
||||
|
@ -10,7 +10,7 @@ values in the form submission (`POST` request):
|
||||
```html
|
||||
<form id="checked-contacts"
|
||||
hx-post="/users"
|
||||
hx-swap="outerHTML settle:3s"
|
||||
hx-swap="innerHTML settle:3s"
|
||||
hx-target="#toast">
|
||||
<table>
|
||||
<thead>
|
||||
@ -36,7 +36,11 @@ values in the form submission (`POST` request):
|
||||
|
||||
The server will bulk-update the statuses based on the values of the checkboxes.
|
||||
We respond with a small toast message about the update to inform the user, and
|
||||
use ARIA to politely announce the update for accessibility.
|
||||
use an `<output>` element to politely announce the update for accessibility. Note
|
||||
that the `<output>` element is appropriate for announcing the result of an action
|
||||
in a specific form, but if you need to announce general-purpose messages that are
|
||||
not connected to a form it would make sense to use an ARIA live region, eg
|
||||
`<p id="toast" aria-live="polite"></p>`.
|
||||
|
||||
```css
|
||||
#toast.htmx-settling {
|
||||
@ -139,7 +143,7 @@ You can see a working example of this code below.
|
||||
}
|
||||
}
|
||||
|
||||
return `<span id="toast" aria-live="polite">Activated ${activated} and deactivated ${deactivated} users</span>`;
|
||||
return `Activated ${activated} and deactivated ${deactivated} users`;
|
||||
});
|
||||
|
||||
// templates
|
||||
@ -148,7 +152,7 @@ You can see a working example of this code below.
|
||||
<form
|
||||
id="checked-contacts"
|
||||
hx-post="/users"
|
||||
hx-swap="outerHTML settle:3s"
|
||||
hx-swap="innerHTML settle:3s"
|
||||
hx-target="#toast"
|
||||
>
|
||||
<table>
|
||||
@ -164,7 +168,7 @@ You can see a working example of this code below.
|
||||
</tbody>
|
||||
</table>
|
||||
<input type="submit" value="Bulk Update" class="btn primary">
|
||||
<span id="toast"></span>
|
||||
<output id="toast"></output>
|
||||
</form>
|
||||
<br>`;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ which is then picked up by `hx-trigger`.
|
||||
// The event is triggered on every trigger for a request, so we need to check if the element
|
||||
// that triggered the request has a hx-confirm attribute, if not we can return early and let
|
||||
// the default behavior happen
|
||||
if (!e.detail.target.hasAttribute('hx-confirm')) return
|
||||
if (!e.detail.elt.hasAttribute('hx-confirm')) return
|
||||
|
||||
// This will prevent the request from being issued to later manually issue it
|
||||
e.preventDefault()
|
||||
|
@ -5,7 +5,19 @@ template = "demo.html"
|
||||
|
||||
When using server-side error handling and validation with forms that include both primitive values and file inputs, the file input's value is lost when the form returns with error messages. Consequently, users are required to re-upload the file, resulting in a less user-friendly experience.
|
||||
|
||||
To overcome the problem of losing file input value in simple cases, you can adopt the following approach:
|
||||
To overcome the problem of losing the file input value, you can use the `hx-preserve` attribute on the `input` element:
|
||||
|
||||
```html
|
||||
<form method="POST" id="binaryForm" enctype="multipart/form-data" hx-swap="outerHTML" hx-target="#binaryForm">
|
||||
<input hx-preserve id="someId" type="file" name="binaryFile">
|
||||
<!-- Other code here, such as input error handling. -->
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
If the file field is returned with errors on it, they will be displayed provided that `hx-preserve` was placed in the `input` only and not the element that would show the errors (e.g. `ol.errorlist`). If in a given circumstance you want the file upload input to return *without* preserving the user's chosen file (for example, because the file was an invalid type), you can manage that on the server side by omitting the `hx-preserve` attribute when the field has the relevant errors.
|
||||
|
||||
Alternatively, you can preserve file inputs after form errors by restructuring the form so that the file input is located outside the area that will be swapped.
|
||||
|
||||
Before:
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
+++
|
||||
title = "Experimental moveBefore() Support"
|
||||
insert_anchor_links = "left"
|
||||
insert_anchor_links = "heading"
|
||||
+++
|
||||
|
||||
This page demonstrates the use of the experimental [`moveBefore()`](https://github.com/whatwg/dom/issues/1255) DOM
|
||||
|
80
www/content/examples/reset-user-input.md
Normal file
80
www/content/examples/reset-user-input.md
Normal file
@ -0,0 +1,80 @@
|
||||
+++
|
||||
title = "Reset user input"
|
||||
template = "demo.html"
|
||||
+++
|
||||
|
||||
This example shows how to easily reset user inputs using [`hx-on`](@/attributes/hx-on.md),
|
||||
allowing users to make multiple requests without having to manually delete their previous inputs.
|
||||
|
||||
The inline script will run on the [`afterRequest`](@/events.md#htmx:afterRequest) event and ensures
|
||||
that the form will reset to its initial state as long as the response has a 20x status code:
|
||||
|
||||
```html
|
||||
<form hx-post="/note"
|
||||
hx-target="#notes"
|
||||
hx-swap="afterbegin"
|
||||
hx-on::after-request="if(event.detail.successful) this.reset()">
|
||||
<div class="form-group">
|
||||
<label>Add a note</label>
|
||||
<input type="text" name="note-text" placeholder="blank canvas">
|
||||
</div>
|
||||
<button class="btn">Add</button>
|
||||
</form>
|
||||
<ul id="notes"><!-- Response will go here --></ul>
|
||||
```
|
||||
|
||||
The `reset()` method is only available on `form` elements.
|
||||
For other elements, the input value can explicitly selected and reset to a default to achieve the same result.
|
||||
The following code is functionally equivalent:
|
||||
|
||||
```html
|
||||
<div>
|
||||
<label>Add a note</label>
|
||||
<input id="note-input" type="text" name="note-text" placeholder="blank canvas">
|
||||
</div>
|
||||
<button class="btn primary"
|
||||
hx-post="/note"
|
||||
hx-target="#note"
|
||||
hx-swap="afterbegin"
|
||||
hx-include="#note-input"
|
||||
hx-on::after-request="if(event.detail.successful)
|
||||
document.getElementById('note-input').value = ''">
|
||||
Add
|
||||
</button>
|
||||
<ul id="notes"><!-- Response will go here --></ul>
|
||||
```
|
||||
|
||||
{{ demoenv() }}
|
||||
|
||||
<script>
|
||||
|
||||
//=========================================================================
|
||||
// Fake Server Side Code
|
||||
//=========================================================================
|
||||
|
||||
// routes
|
||||
init("/demo", function(request) {
|
||||
return formTemplate();
|
||||
})
|
||||
|
||||
onPost("/note", function(request, params) {
|
||||
var note = params['note-text'];
|
||||
if (note) {
|
||||
return `<li>${note}</li>`;
|
||||
}
|
||||
})
|
||||
|
||||
// templates
|
||||
function formTemplate() {
|
||||
return `
|
||||
<form hx-post="/note" hx-target="#notes" hx-swap="afterbegin" hx-on::after-request="if(event.detail.successful) this.reset()">
|
||||
<div class="form-group">
|
||||
<label>Add a note</label>
|
||||
<input type="text" name="note-text" placeholder="blank canvas">
|
||||
</div>
|
||||
<button class="btn primary">Add</button>
|
||||
</form>
|
||||
<ul id="notes"> </ul>`;
|
||||
}
|
||||
</script>
|
||||
|
@ -27,27 +27,149 @@ htmx extensions are split into two categories:
|
||||
|
||||
## Community Extensions
|
||||
|
||||
| Name | Description |
|
||||
|--------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [ajax-header](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/ajax-header/README.md) | Adds an `X-Requested-With` header to all requests made by htmx |
|
||||
| [alpine-morph](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/alpine-morph/README.md) | Alpine.js now has a lightweight [morph plugin](https://alpinejs.dev/plugins/morph) and this extension allows you to use it as the swapping mechanism in htmx which is necessary to retain Alpine state when you have entire Alpine components swapped by htmx. |
|
||||
| [class-tools](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/class-tools/README.md) | The `class-tools` extension allows you to specify CSS classes that will be swapped onto or off of the elements by using a `classes` or `data-classes` attribute. |
|
||||
| [client-side-templates](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/client-side-templates/README.md) | This extension supports transforming a JSON/XML request response into HTML via a client-side template before it is swapped into the DOM. |
|
||||
| [debug](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/debug/README.md) | This extension includes log all htmx events for the element it is on, either through the `console.debug` function or through the `console.log` function with a `DEBUG:` prefix. |
|
||||
| [disable-element](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/disable-element/README.md) | This extension disables an element during an htmx request, when configured on the element triggering the request. Note that this functionality is now part of the core of htmx via the `hx-disabled-elt` attribute |
|
||||
| [event-header](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/event-header/README.md) | This extension adds the `Triggering-Event` header to requests. The value of the header is a JSON serialized version of the event that triggered the request. |
|
||||
| [include-vals](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/include-vals/README.md) | The `include-vals` extension allows you to programmatically include values in a request with a `include-vals` attribute. The value of this attribute is one or more name/value pairs, which will be evaluated as the fields in a javascript object literal. |
|
||||
| [json-enc-custom](https://github.com/Emtyloc/json-enc-custom/blob/main/README.md) | This extension works similarly to json-enc but allows for very complex structures, such as embedding JSON objects, lists, or handling indexes, just by using the name attribute. |
|
||||
| [json-enc](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/json-enc/README.md) | This extension encodes parameters in JSON format instead of url format. |
|
||||
| [loading-states](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/loading-states/README.md) | This extension allows you to easily manage loading states while a request is in flight, including disabling elements, and adding and removing CSS classes. |
|
||||
| [morphdom-swap](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/morphdom-swap/README.md) | Provides a `morph` swap strategy based on the [morphdom](https://github.com/patrick-steele-idem/morphdom/) morphing library. |
|
||||
| [multi-swap](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/multi-swap/README.md) | This extension allows you to swap multiple elements marked from the HTML response. You can also choose for each element which swap method should be used. |
|
||||
| [no-cache](https://github.com/craigharman/htmx-ext-no-cache/blob/master/README.md) | This extension forces HTMX to bypass client caches and make a new request. An `hx-no-cache` header is also added to allow server-side caching to be bypassed. |
|
||||
| [path-deps](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-deps/README.md) | This extension supports expressing inter-element dependencies based on paths, inspired by the [intercooler.js dependencies mechanism](http://intercoolerjs.org/docs.html#dependencies). |
|
||||
| [path-params](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-params/README.md) | This extension uses request parameters to populate path variables. Used parameters are removed so they won't be sent in the query string or body anymore. |
|
||||
| [remove-me](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/remove-me/README.md) | Allows you to remove an element after a specified interval. |
|
||||
| [replace-params](https://github.com/fanelfaa/htmx-ext-replace-params/blob/main/README.md) | This extension uses request parameters to replace existing parameters. If given value is empty string then parameter will be deleted. This extension would be useful in situations like pagination, search that you only want to replace only few parameters instead of reset all parameters. |
|
||||
| [restored](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/restored/README.md) | Triggers an event whenever a back button even is detected while using `hx-boost` |
|
||||
| [safe-nonce](https://github.com/MichaelWest22/htmx-extensions/blob/main/src/safe-nonce/README.md) | The `safe-nonce` extension can be used to improve the security of the application/web-site and help avoid XSS issues by allowing you to return known trusted inline scripts safely |
|
||||
| [signalr](https://github.com/Renerick/htmx-signalr/blob/master/README.md) | Provides bidirectional real-time communication via [SignalR](https://github.com/dotnet/AspNetCore/tree/main/src/SignalR). |
|
||||
| [amz-content-sha256](https://github.com/felipegenef/amz-content-sha256/blob/main/README.md) | HTMX extension for interacting with AWS services that require the content hash as part of the request for data integrity verification. |
|
||||
<search aria-label="Community extensions">
|
||||
<label for="extension-filter" hidden>Search Extensions:</label>
|
||||
<input type="search" id="extension-filter" placeholder="Search Extensions..."
|
||||
_="init
|
||||
set :table to the next <table/>
|
||||
set :initialHeight to *computed-height of :table
|
||||
on keyup
|
||||
if the event's key is 'Escape' then set my value to '' then trigger input end
|
||||
on input
|
||||
repeat in closest <tr/> to <td:first-of-type/> in :table
|
||||
if its textContent.toLowerCase() contains my value.toLowerCase()
|
||||
remove @hidden from it
|
||||
else
|
||||
add @hidden='' to it
|
||||
end
|
||||
end
|
||||
-- hide section header when its section is empty
|
||||
show closest <tr/> to <tbody th/> in :table
|
||||
when (the next <tr:not([hidden])/> from it within the closest <tbody/> to it) exists
|
||||
-- avoid shift by keeping page size constant
|
||||
set *margin-bottom of :table to
|
||||
`calc(${:initialHeight} - ${*computed-height of :table})`">
|
||||
</search>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{% markdown() %} [ajax-header](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/ajax-header/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} Adds an `X-Requested-With` header to all requests made by htmx {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [alpine-morph](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/alpine-morph/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} Alpine.js now has a lightweight [morph plugin](https://alpinejs.dev/plugins/morph) and this extension allows you to use it as the swapping mechanism in htmx which is necessary to retain Alpine state when you have entire Alpine components swapped by htmx. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [attribute-tools](https://github.com/jamcole/htmx-ext-attribute-tools/blob/main/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} The `attribute-tools` extension allows you to specify attributes that will be swapped onto or off of the elements by using an `attributes` or `data-attributes` attribute. (similar to class-tools) {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [class-tools](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/class-tools/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} The `class-tools` extension allows you to specify CSS classes that will be swapped onto or off of the elements by using a `classes` or `data-classes` attribute. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [debug](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/debug/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension will log all htmx events for the element it is on through the `console.debug` function. Note that during dev, using `htmx.logAll()` instead can often be sufficient. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [event-header](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/event-header/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension adds the `Triggering-Event` header to requests. The value of the header is a JSON serialized version of the event that triggered the request. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [loading-states](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/loading-states/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension allows you to easily manage loading states while a request is in flight, including disabling elements, and adding and removing CSS classes. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [morphdom-swap](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/morphdom-swap/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} Provides a `morph` swap strategy based on the [morphdom](https://github.com/patrick-steele-idem/morphdom/) morphing library. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [multi-swap](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/multi-swap/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension allows you to swap multiple elements marked from the HTML response. You can also choose for each element which swap method should be used. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [no-cache](https://github.com/craigharman/htmx-ext-no-cache/blob/master/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension forces HTMX to bypass client caches and make a new request. An `hx-no-cache` header is also added to allow server-side caching to be bypassed. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [path-deps](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-deps/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension supports expressing inter-element dependencies based on paths, inspired by the [intercooler.js dependencies mechanism](http://intercoolerjs.org/docs.html#dependencies). {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [path-params](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-params/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension uses request parameters to populate path variables. Used parameters are removed so they won't be sent in the query string or body anymore. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [remove-me](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/remove-me/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} Allows you to remove an element after a specified interval. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [replace-params](https://github.com/fanelfaa/htmx-ext-replace-params/blob/main/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension uses request parameters to replace existing parameters. If given value is empty string then parameter will be deleted. This extension would be useful in situations like pagination, search that you only want to replace only few parameters instead of reset all parameters. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [restored](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/restored/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} Triggers an event whenever a back button even is detected while using `hx-boost` {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [safe-nonce](https://github.com/MichaelWest22/htmx-extensions/blob/main/src/safe-nonce/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} The `safe-nonce` extension can be used to improve the security of the application/web-site and help avoid XSS issues by allowing you to return known trusted inline scripts safely {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [hx-drag](https://github.com/AjaniBilby/htmx-drag-examples/blob/main/readme.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension allows htmx requests to be sent for drag drop {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr><th scope="rowgroup" colspan="2">Data API</th></tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [client-side-templates](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/client-side-templates/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension supports transforming a JSON/XML request response into HTML via a client-side template before it is swapped into the DOM. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [json-enc](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/json-enc/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension encodes parameters in JSON format instead of url format. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [form-json](https://github.com/xehrad/form-json/blob/main/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} Similar to `json-enc`, but with **type preservation**. Converts form data into structured JSON while maintaining correct types for numbers, booleans, and files (Base64-encoded). Supports nested structures using dot (`.`) or bracket (`[]`) notation. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [json-enc-custom](https://github.com/Emtyloc/json-enc-custom/blob/main/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension works similarly to json-enc but allows for very complex structures, such as embedding JSON objects, lists, or handling indexes, just by using the name attribute. {% end %}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr><th scope="rowgroup" colspan="2">Integrations</th></tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [amz-content-sha256](https://github.com/felipegenef/amz-content-sha256/blob/main/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} HTMX extension for interacting with AWS services that require the content hash as part of the request for data integrity verification. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [signalr](https://github.com/Renerick/htmx-signalr/blob/master/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} Provides bidirectional real-time communication via [SignalR](https://github.com/dotnet/AspNetCore/tree/main/src/SignalR). {% end %}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr><th scope="rowgroup" colspan="2">Legacy</th></tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [disable-element](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/disable-element/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} This extension disables an element during an htmx request, when configured on the element triggering the request. Note that this functionality is now part of the core of htmx via the `hx-disabled-elt` attribute. {% end %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% markdown() %} [include-vals](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/include-vals/README.md) {% end %}</td>
|
||||
<td>{% markdown() %} The `include-vals` extension allows you to programmatically include values in a request with a `include-vals` attribute. Note that this functionality is now part of the core of htmx via the `hx-vals` attribute. {% end %}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -13,10 +13,33 @@ The [`hx-boost`](@/attributes/hx-boost.md) attribute moved htmx closer to this w
|
||||
support for extracting the `title` tag out of head elements was eventually added, but full head tag support has never been
|
||||
a feature of the library. This extension addresses that shortcoming.
|
||||
|
||||
## Install
|
||||
## Installing
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/htmx-ext-head-support@2.0.1/head-support.js"></script>
|
||||
The fastest way to install `head-support` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
|
||||
```HTML
|
||||
<head>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/htmx-ext-head-support@2.0.2" integrity="sha384-cvMqHzjCJsOHgGuyB3sWXaUSv/Krm0BdzjuI1rtkjCbL1l1oHJx+cHyVRJhyuEz0" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body hx-ext="head-support">
|
||||
...
|
||||
```
|
||||
An unminified version is also available at https://unpkg.com/htmx-ext-head-support/dist/head-support.js.
|
||||
|
||||
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `head-support` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-head-support`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
|
||||
|
||||
For npm-style build systems, you can install `head-support` via [npm](https://www.npmjs.com/):
|
||||
```shell
|
||||
npm install htmx-ext-head-support
|
||||
```
|
||||
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-head-support/dist/head-support.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
|
||||
|
||||
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
|
||||
- Install `htmx.org` and `htmx-ext-head-support` via npm
|
||||
- Import both packages to your `index.js`
|
||||
```JS
|
||||
import `htmx.org`;
|
||||
import `htmx-ext-head-support`;
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
@ -4,11 +4,33 @@ title = "htmx 1.x Compatibility Extension"
|
||||
|
||||
The `htmx-1-compat` extension allows you to almost seamlessly upgrade from htmx 1.x to htmx 2.
|
||||
|
||||
## Install
|
||||
## Installing
|
||||
|
||||
```html
|
||||
The fastest way to install `htmx-1-compat` is to load it via a CDN. Remember to always include the core htmx library before the extension and enable the extension.
|
||||
```HTML
|
||||
<head>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/htmx-ext-htmx-1-compat@2.0.0" integrity="sha384-lcvVWaNjF5zPPUeeWmC0OkJ2MLqoWLlkAabuGm+EuMSTfGo5WRyHrNaAp0cJr9Pg" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body hx-ext="htmx-1-compat">
|
||||
...
|
||||
```
|
||||
An unminified version is also available at https://unpkg.com/htmx-ext-htmx-1-compat/dist/htmx-1-compat.js.
|
||||
|
||||
<script src="https://unpkg.com/htmx-ext-htmx-1-compat@2.0.0/htmx-1-compat.js"></script>
|
||||
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `htmx-1-compat` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-htmx-1-compat`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
|
||||
|
||||
For npm-style build systems, you can install `htmx-1-compat` via [npm](https://www.npmjs.com/):
|
||||
```shell
|
||||
npm install htmx-ext-htmx-1-compat
|
||||
```
|
||||
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-htmx-1-compat/dist/htmx-1-compat.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
|
||||
|
||||
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
|
||||
- Install `htmx.org` and `htmx-ext-htmx-1-compat` via npm
|
||||
- Import both packages to your `index.js`
|
||||
```JS
|
||||
import `htmx.org`;
|
||||
import `htmx-ext-htmx-1-compat`;
|
||||
```
|
||||
|
||||
## What it covers
|
||||
|
@ -10,10 +10,35 @@ much smoother transition between the two states.
|
||||
You can use the idiomorph morphing algorithm as a [swapping](@attributes/hx-swap) strategy by including the idiomorph
|
||||
extension.
|
||||
|
||||
## Install
|
||||
## Installing
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/idiomorph@0.3.0/dist/idiomorph-ext.min.js"></script>
|
||||
The fastest way to install `idiomorph` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
|
||||
```HTML
|
||||
<head>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/idiomorph@0.7.3" integrity="sha384-JcorokHTL/m+D6ZHe2+yFVQopVwZ+91GxAPDyEZ6/A/OEPGEx1+MeNSe2OGvoRS9" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/idiomorph@0.7.3/dist/idiomorph-ext.min.js" integrity="sha384-szktAZju9fwY15dZ6D2FKFN4eZoltuXiHStNDJWK9+FARrxJtquql828JzikODob" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body hx-ext="morph">
|
||||
```
|
||||
Unminified versions are also available at:
|
||||
https://unpkg.com/idiomorph/dist/idiomorph.js
|
||||
https://unpkg.com/idiomorph/dist/idiomorph-ext.js
|
||||
|
||||
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `idiomorph` is to simply copy it into your project. Download idiomorph and its htmx extension from `https://unpkg.com/idiomorph` and `https://unpkg.com/idiomorph/dist/idiomorph-ext.min.js`, add them to the appropriate directory in your project and include them where necessary with `<script>` tags.
|
||||
|
||||
For npm-style build systems, you can install `idiomorph` via [npm](https://www.npmjs.com/):
|
||||
```shell
|
||||
npm install idiomorph
|
||||
```
|
||||
After installing, you'll need to use appropriate tooling to bundle `node_modules/idiomorph/dist/idiomorph.js` (or `.min.js`) and `node_modules/idiomorph/dist/idiomorph-ext.js`. For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
|
||||
|
||||
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
|
||||
- Install `htmx.org` and `idiomorph` via npm
|
||||
- Import both packages to your `index.js`
|
||||
```JS
|
||||
import `htmx.org`;
|
||||
import `idiomorph`;
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user