HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0

Martijn Dashorst
Martijn DashorstSoftware engineer at Topicus Onderwijs
HTMX: WEB 1.0
WITH WEB 2.0 BENEFITS
WITHOUT THE GRIFT OF WEB 3.0
MARTIJN DASHORST
# jfall.
WHO HAS HEARD OF
HTMX BEFORE
THIS TALK?
WHO HAS HEARD OF
INTERCOOLER
TURBOLINKS, OR
HOTWIRE?
HTMX is an old idea
that's getting a lot
of new attention
— @bholmesdev
HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0
https://www.youtube.com/watch?v=AOzy44b2gko
https://htmx.org/essays/when-to-use-hypermedia/
https://htmx.org/essays/when-to-use-hypermedia/
https://htmx.org/essays/when-to-use-hypermedia/
https://htmx.org/essays/when-to-use-hypermedia/
HERE WE ARE
I THOUGHT IT WAS A GOOD IDEA TO PITCH A PRESENTATION
FOR JFALL ABOUT HTMX TO GET TO KNOW IT BETTER. SO...
MARTIJN DASHORST
● 18 YEARS @TOPICUS
PARNASSYS, PARRO, SOMTODAY, EDUARTE
SPREEKUUR.NL, DARMKANKER SCREENING,
CORONATEST.NL, FORCE (MORTGAGES)
● BACKEND DEVELOPER BY HEART
● LOVES CREATING FIXING LEGACY SOFTWARE
● CONTRIBUTOR APACHE WICKET
● CO-AUTHORED WICKET IN ACTION
WHY HTMX?
HTMX = WEB 1.0 + WEB 2.0 - WEB 3.0
01
04
02
05
03
WHAT IS HTMX?
HOW DOES IT WORK?
DEMOS
CONCLUSIONS
HTMX
https://htmx.org
intercooler 2013-2020
14kb JavaScript library (gzipped)
- no dependencies
- no build
- attributes
- css classes
- request/response headers
- events
- extensions
- javascript API
HTMX
https://htmx.org
version 1.9.8 (Nov 6, 2023)
14kb JavaScript library (gzipped)
- no dependencies
- no build
- attributes
- css classes
- request/response headers
- events
- extensions
- javascript API
<script src="htmx.js"></script>
HTMX
https://htmx.org
version 1.9.8 (Nov 6, 2023)
14kb JavaScript library (gzipped)
- no dependencies
- no build
- attributes
- css classes
- request/response headers
- events
- extensions
- javascript API
<script src="htmx.js"></script>
<button
hx-get="/counter"
hx-swap="outerHTML">
Click me</button>
HTMX
https://htmx.org
version 1.9.8 (Nov 6, 2023)
14kb JavaScript library (gzipped)
- no dependencies
- no build
- attributes
- css classes
- request/response headers
- events
- extensions
- javascript API
<script src="htmx.js"></script>
<button
hx-get="/counter"
hx-swap="outerHTML"
hx-indicator="#indicator"
>Click me</button>
<i id="indicator"
class="htmx-indicator spinner-border">
</i>
By Carson Gross
https://bigsky.software
A Return to Hypermedia –
"Curing Your JavaScript Fatigue
Using The Original Architecture
Of The Web"
htmx: Writing JavaScript to Avoid
Writing JavaScript
HTMx: Building modern web
applications without JS
HTMX
one day code base understandable and grug can
get work done, everything good!
next day impossible: complexity demon spirit has
entered code and very dangerous situation!
— https://grugbrain.dev/
WHY HTMX?
HTMX = WEB 1.0 + WEB 2.0 - WEB 3.0
01
04
02
05
03
WHAT IS HTMX?
HOW DOES IT WORK?
DEMOS
CONCLUSIONS
BUILDING A REST API
FOR A WEB PORTAL AND NATIVE APP
MY EXPERIENCES
REST server
xml
html
xml
BUILDING A REST API
FOR A WEB PORTAL
MY EXPERIENCES
BUILDING A REST API
FOR A WEB PORTAL
MY EXPERIENCES
data format for API:
JSON or XML
BUILDING A REST API
FOR A WEB PORTAL
MY EXPERIENCES
data format for API:
JSON or XML
POST or PUT?
BUILDING A REST API
FOR A WEB PORTAL
MY EXPERIENCES
data format for API:
JSON or XML
caching the resources
POST or PUT?
BUILDING A REST API
FOR A WEB PORTAL
MY EXPERIENCES
data format for API:
JSON or XML
caching the resources
paging of results
POST or PUT?
BUILDING A REST API
FOR A WEB PORTAL
MY EXPERIENCES
data format for API:
JSON or XML
versioning the API
caching the resources
paging of results
POST or PUT?
BUILDING A REST API
FOR A WEB PORTAL
MY EXPERIENCES
data format for API:
JSON or XML
versioning the API
caching the resources
paging of results
POST or PUT?
POST to /resource or /
resource/new ?
BUILDING A REST API
FOR A WEB PORTAL
MY EXPERIENCES
data format for API:
JSON or XML
versioning the API
caching the resources
paging of results
POST or PUT?
POST to /resource or /
resource/new ?
Links between
resources?
BUILDING A REST API
FOR A WEB PORTAL
MY EXPERIENCES
data format for API:
JSON or XML
versioning the API
caching the resources
paging of results
POST or PUT?
POST to /resource or /
resource/new ?
Links between
resources?
Complex UIs, more
generic REST resources,
God Objects
A FEW
MOMENTS
YEARS
LATER
SPA architecture =
JSON + REST
HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0
Dear Roy,
What is the best practice for versioning a REST API?
DON'T
— Roy T. Fielding
Dear Roy,
What is the best practice for versioning a REST API?
https:/
/www.slideshare.net/evolve_conference/201308-fielding-evolve#31
Dear Roy,
What is the best practice for versioning a REST API?
https:/
/www.slideshare.net/evolve_conference/201308-fielding-evolve#31
when you use hypertext as the engine of application
state, you don't need it
— Roy T. Fielding
SPA architecture =
JSON + RPC
"REST"
SPA architecture =
JSON + RPC
"REST"
WHY?
WHY CAN ONLY <A> AND <FORM> PERFORM
THOSE REQUESTS?
WHY ONLY GET & POST REQUESTS?
HTML 2.0 specification
https:/
/datatracker.ietf.org/doc/html/rfc1866
1995
https:/
/datatracker.ietf.org/doc/html/rfc1866
1995
WHY HTMX?
HTMX = WEB 1.0 + WEB 2.0 - WEB 3.0
01
04
02
05
03
WHAT IS HTMX?
HOW DOES IT WORK?
DEMOS
CONCLUSIONS
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
credit: @htmx.org@twitter
https:/
/twitter.com/htmx_org/status/1700259958405869711
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
<button class="btn btn-success"
onclick="
const req = new XMLHttpRequest();
req.onload = (e) => {
const admin = document.getElementById('admin');
const newAdmin = req.responseXML.getElementById('admin');
admin.innerHTML = newAdmin.innerHTML;
};
req.responseType = 'document';
req.open('GET', '/admin');
req.overrideMimeType('text/html');
req.send();"
>Click me</button>
<section id="admin"></section>
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
<button class="btn btn-primary"
hx-get="/admin"
hx-select="#admin"
hx-target="#admin"
>Click me</button>
<section id="admin"></section>
GETTING THE SCRIPT
<script src="https://unpkg.com/htmx.org@1.9.7"></script>
Quick and dirty
TIP: USE WEBJARS
i
Using webjars
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>htmx.org</artifactId>
<version>1.9.7</version>
</dependency>
<script src="/webjars/htmx.org/1.9.7/dist/htmx.js"></script>
/META-INF/resources/webjars
«classpath»
SEE ALSO: https:/
/www.baeldung.com/maven-webjars
TIP: WEBJARS + LOCATOR
i
Spring
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.48</version>
</dependency>
<script src="/webjars/htmx.org/dist/htmx.js"></script>
no version
Quarkus
quarkus ext add io.quarkus:quarkus-webjars-locator
SEE ALSO: https:/
/www.baeldung.com/maven-webjars
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
Attributes instruct
HTMX what to do with
what element
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
Attributes instruct
HTMX what to do with
what element
hx-boost="true|false"
hx-get="/count"
hx-post="/count"
hx-target="#counter"
hx-swap="innerHTML|outerHTML|afterend|..."
hx-trigger="click|load|every 1s"
hx-select="#htmx-badge"
hx-swap-oob="true|outerHTML:selector"
hx-delete|patch|put="/url"
...
ATTRIBUTES – hx-boost
<nav hx-boost="true">
<li><a href="/menu1">Menu 1</a></li>
<li><a href="/menu2">Menu 2</a></li>
<li><a href="/menu3">Menu 3</a></li>
<li><a href="/menu4">Menu 4</a></li>
</nav>
Replaces the GET/POST request using AJAX and replaces the body using
innerHTML swap.
inherited
ATTRIBUTES – hx-get
<button hx-get="/speaker/1">Get speaker 1</button>
Issue a GET request to the specified URL and swap the returned HTML into
the DOM using a swap strategy
ATTRIBUTES – hx-get
<button hx-get="/speaker/1">Get speaker 1</button>
Issue a GET request to the specified URL and swap the returned HTML into
the DOM using a swap strategy
ATTRIBUTES – hx-get
<button hx-get="/speaker/1">Get speaker 1</button>
Issue a GET request to the specified URL and swap the returned HTML into
the DOM using a swap strategy
<div id="speaker">...</div>
ATTRIBUTES – hx-get
<button hx-get="/speaker/1">Get speaker 1</button>
Issue a GET request to the specified URL and swap the returned HTML into
the DOM using a swap strategy
<div id="speaker">...</div>
<button hx-get="/speaker/1">
Get speaker 1
</button>
ATTRIBUTES – hx-get
<button hx-get="/speaker/1">Get speaker 1</button>
Issue a GET request to the specified URL and swap the returned HTML into
the DOM using a swap strategy
<div id="speaker">...</div>
<button hx-get="/speaker/1">
<div id="speaker">...</div>
</button>
ATTRIBUTES – hx-swap
<button hx-get="/speaker/1"
hx-swap="outerHTML">Get speaker 1</button>
Controls how the HTML is swapped into the DOM.
innerHTML, outerHTML, beforebegin, afterbegin, beforeend, afterend, delete, none
<div id="speaker">...</div>
<button hx-get="/speaker/1"
hx-swap="outerHTML">Get speaker 1</button>
inherited
ATTRIBUTES – hx-swap
<button hx-get="/speaker/1"
hx-swap="outerHTML">Get speaker 1</button>
Controls how the HTML is swapped into the DOM.
innerHTML, outerHTML, beforebegin, afterbegin, beforeend, afterend, delete, none
<div id="speaker">...</div>
<div id="speaker">...</div>
ATTRIBUTES – hx-target
Controls which element in the DOM gets swapped with the returned HTML
using a swapping strategy.
inherited
<button hx-get="/speaker/1" hx-swap="outerHTML"
hx-target="#speaker">Get speaker 1</button>
<div id="speaker">No content</div>
ATTRIBUTES – hx-target
Controls which element in the DOM gets swapped with the returned HTML
using a swapping strategy.
inherited
<button hx-get="/speaker/1" hx-swap="outerHTML"
hx-target="#speaker">Get speaker 1</button>
<div id="speaker">No content</div>
<div id="speaker">Johan Janssen</div>
<div id="speaker">No content</div>
ATTRIBUTES – hx-target
Controls which element in the DOM gets swapped with the returned HTML
using a swapping strategy.
inherited
<button hx-get="/speaker/1" hx-swap="outerHTML"
hx-target="#speaker">Get speaker 1</button>
<div id="speaker">No content</div>
<div id="speaker" class="card">Johan Janssen</div>
<div id="speaker">No content</div>
ATTRIBUTES – hx-target
Controls which element in the DOM gets swapped with the returned HTML
using a swapping strategy.
inherited
<button hx-get="/speaker/1" hx-swap="outerHTML"
hx-target="#speaker">Get speaker 1</button>
<div id="speaker">No content</div>
<div id="speaker" class="card">Johan Janssen</div>
<div id="speaker" class="card">Johan Janssen</div>
ATTRIBUTES – hx-target
Inherited???
inherited
<menu>
<a hx-get="/speaker/1" hx-target="#speaker">Get speaker 1
<a hx-get="/speaker/2" hx-target="#speaker">Get speaker 2
<a hx-get="/speaker/3" hx-target="#speaker">Get speaker 3
<a hx-get="/speaker/4" hx-target="#speaker">Get speaker 4
</menu>
ATTRIBUTES – hx-target
hx-target is inherited and can be placed on a parent element.
inherited
<menu hx-target="#speaker">
<a hx-get="/speaker/1">Get speaker 1</a>
<a hx-get="/speaker/2">Get speaker 2</a>
<a hx-get="/speaker/3">Get speaker 3</a>
<a hx-get="/speaker/4">Get speaker 4</a>
</menu>
MORE ABOUT ATTRIBUTES
INHERITED ATTRIBUTES
NO JAVASCRIPT REQ'D
Removes repetition of
common attributes, such as
the target, swap method,
selecting elements etc.
Can be overridden by
hx-disinherit
With just the tags you can
create rich dynamically
updating pages.
Slap on some attributes and
continue.
In the HTML response you can
add more updates by using
out-of-band swaps
(hx-swap-oob)
UPDATE MULTIPLE ELEMENTS
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
Attributes instruct
HTMX what to do with
what element
Server may need to
distinguish between
normal and HTMX
request: Headers
HX-Request always true
HX-Boosted indicates hx-boost request
HX-Current-URL ...
...
REQUEST HEADERS
RESPONSE HEADERS
HX-Redirect client-side redirect
HX-Refresh client-side full refresh of the page
HX-Retarget modify the hx-target
HX-Trigger trigger client-side events
...
HOW DOES IT WORK – HEADERS
Convenient for deciding to return a
partial or a full page
DETECT HTMX REQUEST
MODIFY REQUESTS
Change the target, or swapping
mechanism, send a redirect or replace
the URL in the location bar. And more.
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
Attributes instruct
HTMX what to do with
what element
Server may need to
distinguish between
normal and HTMX
request: Headers
When a HTMX request
is in flight, feedback is
wanted for Users:
classes
htmx-indicator toggles visibility (opacity:1)
htmx-request applied to hx-indicator during request
htmx-added
htmx-swapping
htmx-settling
CLASSES
HOW DOES IT WORK – CLASSES
Convenient for deciding to return a
partial or a full page
INDICATOR
ANIMATIONS
Animate swapping of elements for a
more rich experience.
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
Attributes instruct
HTMX what to do with
what element
Server may need to
distinguish between
normal and HTMX
request: Headers
When a HTMX request
is in flight, feedback is
wanted for Users:
classes
Complex needs for
reacting to interactions,
swapping, requests &
responses: events
HOW DOES IT WORK? – EVENTS
htmx:abort
htmx:afterOnLoad
htmx:afterProcessNode
htmx:afterRequest
htmx:afterSettle
htmx:afterSwap
htmx:beforeCleanupElement
htmx:beforeOnLoad
htmx:beforeProcessNode
htmx:beforeRequest
htmx:beforeSwap
htmx:beforeSend
htmx:configRequest
htmx:confirm
htmx:historyCacheError
htmx:historyCacheMiss
htmx:historyCacheMissError
htmx:historyCacheMissLoad
htmx:historyRestore
htmx:beforeHistorySave
htmx:load
htmx:noSSESourceError
htmx:onLoadError
htmx:oobAfterSwap
htmx:oobBeforeSwap
htmx:oobErrorNoTarget
htmx:prompt
htmx:pushedIntoHistory
htmx:responseError
htmx:sendError
htmx:sseError
htmx:sseOpen
htmx:swapError
htmx:targetError
htmx:timeout
htmx:validation:validate
htmx:validation:failed
htmx:validation:halted
htmx:xhr:abort
htmx:xhr:loadend
htmx:xhr:loadstart
htmx:xhr:progress
HOW DOES IT WORK? – EVENTS
before/after swaps
occurred, after DOM has
settled, out-of-bounds
swaps, etc.
Anything relevant to
modifying using the
history API
The lifecycle of the XHR
request: progress,
loadend, loadstart, etc.
Errors sending a request,
receiving a response,
targetting an element, etc.
SWAPPING HISTORY VALIDATION
Triggered when client side
validation is performed,
failed.
Configure the request
before it is sent to the
server, response
processing
XHR ERRORS REQUESTS
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
Attributes instruct
HTMX what to do with
what element
Server may need to
distinguish between
normal and HTMX
request: Headers
When a HTMX request
is in flight, feedback is
wanted for Users:
classes
Complex needs for
reacting to interactions,
swapping, requests &
responses: events
In the events some
annoying JavaScript
needs to be executed
(e.g. add class):
JavaScript library
FINDING ELEMENTS
HOW DOES IT WORK? – JAVASCRIPT
addClass(), removeClass(),
toggleClass(), takeClass()
remove(), values()
EVENTS ON ELEMENTS
on(), off(), trigger()
closest(), find(), findAll() config, ajax(), process()
defineExtension(), removeExtension()
WORKING WITH CLASSES WORKING WITH ELEMENTS
INTERNALS & EXTENSIONS
HOW DOES IT WORK? – JAVASCRIPT
Documents
Projects
Members
Admin
Gangplank lookout killick jack yo-ho-ho Sea Legs yard
marooned interloper yawl. Gabion fire ship Brethren of the
Coast lanyard chase Cat o'nine tails dead men tell no tales
barque yawl Nelsons folly.
<ul class="list-group" hx-target="#content"
hx-on::after-request="htmx.takeClass(event.detail.elt, 'active')">
<li class="list-group-item" hx-get="/sub/Documents">Documents</li>
<li class="list-group-item" hx-get="/sub/Projects">Projects</li>
<li class="list-group-item" hx-get="/sub/Members">Members</li>
<li class="list-group-item" hx-get="/sub/Admin">Admin</li>
</ul>
<section id="content"></section>
HOW DOES IT WORK?
Any HTML element can
interact as an
hypermedia component
Attributes instruct
HTMX what to do with
what element
Server may need to
distinguish between
normal and HTMX
request: Headers
When a HTMX request
is in flight, feedback is
wanted for Users:
classes
Complex needs for
reacting to interactions,
swapping, requests &
responses: events
In the events some
annoying JavaScript
needs to be executed
(e.g. add class):
JavaScript library
We need to use a
different HTML
morphing algo,
serverside sent events
or web sockets:
extensions
FINALLY HATEOAS?
HTMX = WEB 1.0 + WEB 2.0 - WEB 3.0
01
04
02
05
03
WHAT IS HTMX?
HOW DOES IT WORK?
DEMOS
CONCLUSIONS
Examples
Fun with flags game
fun with flags
● quarkus
● qute
● websockets
● htmx
● htmx-ws
● bootstrap
● flag=icons
github.com/dashorst/funwithflags
HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0
HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0
WEBSOCKETS FTW!
@ServerEndpoint("/game/{player}")
public class FunWithFlagsWebSocket {
@OnOpen
public void onOpen(Session session, @PathParam("player") String encodedPlayer) {
var player = URLDecoder.decode(encodedPlayer, StandardCharsets.UTF_8);
sessions.put(player, session);
funWithFlagsGame.registerPlayer(session, player);
}
WEBSOCKETS FTW!
@ServerEndpoint("/game/{player}")
public class FunWithFlagsWebSocket {
@OnOpen
public void onOpen(Session session, @PathParam("player") String encodedPlayer) {
var player = URLDecoder.decode(encodedPlayer, StandardCharsets.UTF_8);
sessions.put(player, session);
funWithFlagsGame.registerPlayer(session, player);
}
<div id="game" hx-ext="ws" ws-connect="/game/{playername}" class="row">
<div class="col-12 text-center">
<h3>Joining lobby...</h3>
{#fragment id=lobby}
<ul class="list-group" id="lobby-list">
{#for player in players}
<li class="list-group-item {#if player.name == playername} list-group-
{/for}
</ul>
{/fragment}
<p>Waiting for other players to join!</p>
</div>
</div>
WEBSOCKETS FTW!
@ServerEndpoint("/game/{player}")
public class FunWithFlagsWebSocket {
@OnOpen
public void onOpen(Session session, @PathParam("player") String encodedPlayer) {
var player = URLDecoder.decode(encodedPlayer, StandardCharsets.UTF_8);
sessions.put(player, session);
funWithFlagsGame.registerPlayer(session, player);
}
<div id="game" hx-ext="ws" ws-connect="/game/{playername}" class="row">
<div class="col-12 text-center">
<h3>Joining lobby...</h3>
{#fragment id=lobby}
<ul class="list-group" id="lobby-list">
{#for player in players}
<li class="list-group-item {#if player.name == playername} list-group-
{/for}
</ul>
{/fragment}
<p>Waiting for other players to join!</p>
</div>
</div>
hx-ext="ws" ws-connect="/game/{playername}"
@ServerEndpoint("/game/{player}")
@OnOpen
public void onOpen(Session session, @PathParam("player")
SENDING TURN START TO PLAYERS
public void onTurnStarted(@Observes TurnStarted event) {
var game = event.game();
var turn = event.turn();
var countryToGuess = turn.countryToGuess();
for (Player player : game.players()) {
var turnHtml = Templates.game$turnPartial(player, game, turn, countryToGuess)
.render();
var rankHtml = Templates.game$rankingPartial(player, game, turn).render();
player.session().getAsyncRemote().sendObject(turnHtml + "n" + rankHtml);
}
}
<div id="turn" class="col-12 col-lg-6 offset-lg-3">
<h2>Round {game.turnNumber} of {game.numberOfTurns}</h2>
...
</div>
<div id="in-game-ranking" class="col-12 col-md-3">
<h3>Ranking round {game.turnNumber}</h3>
...
</div>
SENDING TURN START TO PLAYERS
public void onTurnStarted(@Observes TurnStarted event) {
var game = event.game();
var turn = event.turn();
var countryToGuess = turn.countryToGuess();
for (Player player : game.players()) {
var turnHtml = Templates.game$turnPartial(player, game, turn, countryToGuess)
.render();
var rankHtml = Templates.game$rankingPartial(player, game, turn).render();
player.session().getAsyncRemote().sendObject(turnHtml + "n" + rankHtml);
}
}
<div id="turn" class="col-12 col-lg-6 offset-lg-3">
<h2>Round {game.turnNumber} of {game.numberOfTurns}</h2>
...
</div>
<div id="in-game-ranking" class="col-12 col-md-3">
<h3>Ranking round {game.turnNumber}</h3>
...
</div>
SENDING TURN START TO PLAYERS
public void onTurnStarted(@Observes TurnStarted event) {
var game = event.game();
var turn = event.turn();
var countryToGuess = turn.countryToGuess();
for (Player player : game.players()) {
var turnHtml = Templates.game$turnPartial(player, game, turn, countryToGuess)
.render();
var rankHtml = Templates.game$rankingPartial(player, game, turn).render();
player.session().getAsyncRemote().sendObject(turnHtml + "n" + rankHtml);
}
}
<div id="turn" class="col-12 col-lg-6 offset-lg-3">
<h2>Round {game.turnNumber} of {game.numberOfTurns}</h2>
...
</div>
<div id="in-game-ranking" class="col-12 col-md-3">
<h3>Ranking round {game.turnNumber}</h3>
...
</div>
SENDING TURN START TO PLAYERS
public void onTurnStarted(@Observes TurnStarted event) {
var game = event.game();
var turn = event.turn();
var countryToGuess = turn.countryToGuess();
for (Player player : game.players()) {
var turnHtml = Templates.game$turnPartial(player, game, turn, countryToGuess)
.render();
var rankHtml = Templates.game$rankingPartial(player, game, turn).render();
player.session().getAsyncRemote().sendObject(turnHtml + "n" + rankHtml);
}
}
<div id="turn" class="col-12 col-lg-6 offset-lg-3">
<h2>Round {game.turnNumber} of {game.numberOfTurns}</h2>
...
</div>
<div id="in-game-ranking" class="col-12 col-md-3">
<h3>Ranking round {game.turnNumber}</h3>
...
</div>
SENDING TURN START TO PLAYERS
public void onTurnStarted(@Observes TurnStarted event) {
var game = event.game();
var turn = event.turn();
var countryToGuess = turn.countryToGuess();
for (Player player : game.players()) {
var turnHtml = Templates.game$turnPartial(player, game, turn, countryToGuess)
.render();
var rankHtml = Templates.game$rankingPartial(player, game, turn).render();
player.session().getAsyncRemote().sendObject(turnHtml + "n" + rankHtml);
}
}
<div id="turn" class="col-12 col-lg-6 offset-lg-3">
<h2>Round {game.turnNumber} of {game.numberOfTurns}</h2>
...
</div>
<div id="in-game-ranking" class="col-12 col-md-3">
<h3>Ranking round {game.turnNumber}</h3>
...
</div>
DEMO
FINALLY HATEOAS?
HTMX = WEB 1.0 + WEB 2.0 - WEB 3.0
01
04
02
05
03
WHAT IS HTMX?
HOW DOES IT WORK?
DEMOS
CONCLUSIONS
BACKEND DEVS REJOICE
HTMX: WEB 1.0 + WEB 2.0 - WEB 3.0
No more general APIs between your
user interface and your back end. Use
hypermedia as the engine for your
application state
Hypermedia On Whatever You'd Like.
Backend agnostic! Spring Boot,
Quarkus, JSP, PHP, DJango, Ruby on
Rails, ...
HTMX allows you to build rich front
ends without having to learn rich front
end technologies.
Just one script tag and your back end.
Written in whatever you'd like.
REST WITH HATEOAS HOWL STACK
REDUCE THE CHURN
It took me ~2 months spare time to write Fun with
Flags, but most of that time was spent figuring out
the game mechanics, not HTMX integration.
9/10, would use again1
1 (already using it in other projects)
HTMX.ORG
https://htmx.org
Great documentation,
examples and essays on the
fundamentals of HTMX and
hypermedia.
WARNING
Also memes
HYPERMEDIA
SYSTEMS
https://hypermedia.systems
Best explanation of
Hypermedia, REST,
RESTFUL and HATEOAS.
WARNING
Risk of wanting to use HTMX after reading
https:/
/fun-with-flags.eduarte.dev
Wanna play?
Scan this
CREDITS: This presentation template was created by Slidesgo, and
includes icons by Flaticon, and infographics & images by Freepik
THANKS!
Do you have any questions?
martijn.dashorst@topicus.nl
@dashorst@mastodon.social
martijndashorst.com
CREDITS
HTMX was conceived by Carson Gross
Title "HTMX: WEB 1.0 WITH WEB 2.0 BENEFITS WITHOUT THE GRIFT OF WEB 3.0"
was inspired by The Primagen
Template by slidesgo and includes images from flaticon and icons from freepik
Thanks for your attention
Please rate my session in the J-Fall app
# jfall.
fun-with-flags.eduarte.dev HTMX: web 1.0 + web 2.0 - web 3.0
Martijn Dashorst
topicus
1 of 100

Recommended

An Introduction to the SAM Pattern by
An Introduction to the SAM PatternAn Introduction to the SAM Pattern
An Introduction to the SAM PatternJean-Jacques Dubray
1.3K views24 slides
Implementing DDD with C# by
Implementing DDD with C#Implementing DDD with C#
Implementing DDD with C#Pascal Laurin
36.3K views24 slides
Vertical Slicing Architectures by
Vertical Slicing ArchitecturesVertical Slicing Architectures
Vertical Slicing ArchitecturesVictor Rentea
1.2K views55 slides
principles of object oriented class design by
principles of object oriented class designprinciples of object oriented class design
principles of object oriented class designNeetu Mishra
4.9K views63 slides
Alpine.js: the outsider Javascript framework by
Alpine.js: the outsider Javascript frameworkAlpine.js: the outsider Javascript framework
Alpine.js: the outsider Javascript frameworkCommit University
232 views23 slides
Spring Cloud Workshop by
Spring Cloud WorkshopSpring Cloud Workshop
Spring Cloud WorkshopYongSung Yoon
2.8K views111 slides

More Related Content

What's hot

Sqlアンチパターン(メタデータトリブル) by
Sqlアンチパターン(メタデータトリブル)Sqlアンチパターン(メタデータトリブル)
Sqlアンチパターン(メタデータトリブル)Tomoaki Uchida
4.1K views18 slides
#살아있다 #자프링외길12년차 #코프링2개월생존기 by
#살아있다 #자프링외길12년차 #코프링2개월생존기#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기Arawn Park
1.8K views82 slides
Quick tour of PHP from inside by
Quick tour of PHP from insideQuick tour of PHP from inside
Quick tour of PHP from insidejulien pauli
11.3K views67 slides
Spring Boot Persistence Best Practices - How to effectively shape the @OneToM... by
Spring Boot Persistence Best Practices - How to effectively shape the @OneToM...Spring Boot Persistence Best Practices - How to effectively shape the @OneToM...
Spring Boot Persistence Best Practices - How to effectively shape the @OneToM...Anghel Leonard
772 views21 slides
Clean code by
Clean codeClean code
Clean codeArturo Herrero
70K views88 slides

What's hot(20)

Sqlアンチパターン(メタデータトリブル) by Tomoaki Uchida
Sqlアンチパターン(メタデータトリブル)Sqlアンチパターン(メタデータトリブル)
Sqlアンチパターン(メタデータトリブル)
Tomoaki Uchida4.1K views
#살아있다 #자프링외길12년차 #코프링2개월생존기 by Arawn Park
#살아있다 #자프링외길12년차 #코프링2개월생존기#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기
Arawn Park1.8K views
Quick tour of PHP from inside by julien pauli
Quick tour of PHP from insideQuick tour of PHP from inside
Quick tour of PHP from inside
julien pauli11.3K views
Spring Boot Persistence Best Practices - How to effectively shape the @OneToM... by Anghel Leonard
Spring Boot Persistence Best Practices - How to effectively shape the @OneToM...Spring Boot Persistence Best Practices - How to effectively shape the @OneToM...
Spring Boot Persistence Best Practices - How to effectively shape the @OneToM...
Anghel Leonard772 views
AEM Best Practices for Component Development by Gabriel Walt
AEM Best Practices for Component DevelopmentAEM Best Practices for Component Development
AEM Best Practices for Component Development
Gabriel Walt17.7K views
Domain Driven Design and Hexagonal Architecture with Rails by Declan Whelan
Domain Driven Design and Hexagonal Architecture with RailsDomain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with Rails
Declan Whelan8.7K views
AEM and Sling by Lo Ki
AEM and SlingAEM and Sling
AEM and Sling
Lo Ki1.9K views
React & Redux JS by Hamed Farag
React & Redux JS React & Redux JS
React & Redux JS
Hamed Farag732 views
A Journey from Hexagonal Architecture to Event Sourcing - SymfonyCon Cluj 2017 by Carlos Buenosvinos
A Journey from Hexagonal Architecture to Event Sourcing - SymfonyCon Cluj 2017A Journey from Hexagonal Architecture to Event Sourcing - SymfonyCon Cluj 2017
A Journey from Hexagonal Architecture to Event Sourcing - SymfonyCon Cluj 2017
Carlos Buenosvinos3.4K views
OPcache の最適化器の今 by y-uti
OPcache の最適化器の今OPcache の最適化器の今
OPcache の最適化器の今
y-uti10.2K views
Rethinking Best Practices by floydophone
Rethinking Best PracticesRethinking Best Practices
Rethinking Best Practices
floydophone130.2K views
Шаблоны разработки ПО. Часть 3. Шаблоны GoF by Sergey Nemchinsky
Шаблоны разработки ПО. Часть 3. Шаблоны GoFШаблоны разработки ПО. Часть 3. Шаблоны GoF
Шаблоны разработки ПО. Часть 3. Шаблоны GoF
Sergey Nemchinsky6.4K views
Angular by sridhiya
AngularAngular
Angular
sridhiya154 views

Similar to HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0

HTTP/2 : why upgrading the web? - DjangoCon Europe 2016 Budapest by
HTTP/2 : why upgrading the web? - DjangoCon Europe 2016 BudapestHTTP/2 : why upgrading the web? - DjangoCon Europe 2016 Budapest
HTTP/2 : why upgrading the web? - DjangoCon Europe 2016 BudapestQuentin Adam
2.1K views83 slides
HTTP/2 : why upgrading the web? - apidays Paris by
HTTP/2 : why upgrading the web? - apidays ParisHTTP/2 : why upgrading the web? - apidays Paris
HTTP/2 : why upgrading the web? - apidays ParisQuentin Adam
2.2K views58 slides
Fulfilling the Hypermedia Constraint via HTTP OPTIONS, The HTTP Vocabulary In... by
Fulfilling the Hypermedia Constraint via HTTP OPTIONS, The HTTP Vocabulary In...Fulfilling the Hypermedia Constraint via HTTP OPTIONS, The HTTP Vocabulary In...
Fulfilling the Hypermedia Constraint via HTTP OPTIONS, The HTTP Vocabulary In...ruyalarcon
1.5K views24 slides
Http2: why the web is upgrading? - bdx.io 2015 by
Http2: why the web is upgrading?   - bdx.io 2015Http2: why the web is upgrading?   - bdx.io 2015
Http2: why the web is upgrading? - bdx.io 2015Quentin Adam
4.3K views74 slides
Web-Designing Workshop Day 2 by
Web-Designing Workshop Day 2Web-Designing Workshop Day 2
Web-Designing Workshop Day 2dk201020
1.1K views45 slides
POX to HATEOAS: Our Company's Journey Building a Hypermedia API by
POX to HATEOAS: Our Company's Journey Building a Hypermedia APIPOX to HATEOAS: Our Company's Journey Building a Hypermedia API
POX to HATEOAS: Our Company's Journey Building a Hypermedia APILuke Stokes
7.1K views33 slides

Similar to HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0(20)

HTTP/2 : why upgrading the web? - DjangoCon Europe 2016 Budapest by Quentin Adam
HTTP/2 : why upgrading the web? - DjangoCon Europe 2016 BudapestHTTP/2 : why upgrading the web? - DjangoCon Europe 2016 Budapest
HTTP/2 : why upgrading the web? - DjangoCon Europe 2016 Budapest
Quentin Adam2.1K views
HTTP/2 : why upgrading the web? - apidays Paris by Quentin Adam
HTTP/2 : why upgrading the web? - apidays ParisHTTP/2 : why upgrading the web? - apidays Paris
HTTP/2 : why upgrading the web? - apidays Paris
Quentin Adam2.2K views
Fulfilling the Hypermedia Constraint via HTTP OPTIONS, The HTTP Vocabulary In... by ruyalarcon
Fulfilling the Hypermedia Constraint via HTTP OPTIONS, The HTTP Vocabulary In...Fulfilling the Hypermedia Constraint via HTTP OPTIONS, The HTTP Vocabulary In...
Fulfilling the Hypermedia Constraint via HTTP OPTIONS, The HTTP Vocabulary In...
ruyalarcon1.5K views
Http2: why the web is upgrading? - bdx.io 2015 by Quentin Adam
Http2: why the web is upgrading?   - bdx.io 2015Http2: why the web is upgrading?   - bdx.io 2015
Http2: why the web is upgrading? - bdx.io 2015
Quentin Adam4.3K views
Web-Designing Workshop Day 2 by dk201020
Web-Designing Workshop Day 2Web-Designing Workshop Day 2
Web-Designing Workshop Day 2
dk2010201.1K views
POX to HATEOAS: Our Company's Journey Building a Hypermedia API by Luke Stokes
POX to HATEOAS: Our Company's Journey Building a Hypermedia APIPOX to HATEOAS: Our Company's Journey Building a Hypermedia API
POX to HATEOAS: Our Company's Journey Building a Hypermedia API
Luke Stokes7.1K views
HTTP/2 by divarvel
HTTP/2HTTP/2
HTTP/2
divarvel806 views
The end of polling : why and how to transform a REST API into a Data Streamin... by Audrey Neveu
The end of polling : why and how to transform a REST API into a Data Streamin...The end of polling : why and how to transform a REST API into a Data Streamin...
The end of polling : why and how to transform a REST API into a Data Streamin...
Audrey Neveu2.3K views
Hanoi php day 2008 - 05. nguyen hai nhat huy - building-restful-web-service-w... by Nguyen Duc Phu
Hanoi php day 2008 - 05. nguyen hai nhat huy - building-restful-web-service-w...Hanoi php day 2008 - 05. nguyen hai nhat huy - building-restful-web-service-w...
Hanoi php day 2008 - 05. nguyen hai nhat huy - building-restful-web-service-w...
Nguyen Duc Phu1.1K views
The Power of Open Data by Phil Windley
The Power of Open DataThe Power of Open Data
The Power of Open Data
Phil Windley553 views
Together Cheerfully to Walk with Hypermedia by Vladimir Tsukur
Together Cheerfully to Walk with HypermediaTogether Cheerfully to Walk with Hypermedia
Together Cheerfully to Walk with Hypermedia
Vladimir Tsukur4.7K views
HTTP colon slash slash: end of the road? @ CakeFest 2013 in San Francisco by Alessandro Nadalin
HTTP colon slash slash: end of the road? @ CakeFest 2013 in San FranciscoHTTP colon slash slash: end of the road? @ CakeFest 2013 in San Francisco
HTTP colon slash slash: end of the road? @ CakeFest 2013 in San Francisco
Alessandro Nadalin3.8K views
KMUTNB - Internet Programming 2/7 by phuphax
KMUTNB - Internet Programming 2/7KMUTNB - Internet Programming 2/7
KMUTNB - Internet Programming 2/7
phuphax870 views
From ZERO to REST in an hour by Cisco DevNet
From ZERO to REST in an hour From ZERO to REST in an hour
From ZERO to REST in an hour
Cisco DevNet3.3K views

More from Martijn Dashorst

From Floppy Disks to Cloud Deployments by
From Floppy Disks to Cloud DeploymentsFrom Floppy Disks to Cloud Deployments
From Floppy Disks to Cloud DeploymentsMartijn Dashorst
98 views102 slides
SOLID principles by
SOLID principlesSOLID principles
SOLID principlesMartijn Dashorst
366 views72 slides
Converting 85% of Dutch Primary Schools from Oracle to PostgreSQL by
Converting 85% of Dutch Primary Schools from Oracle to PostgreSQLConverting 85% of Dutch Primary Schools from Oracle to PostgreSQL
Converting 85% of Dutch Primary Schools from Oracle to PostgreSQLMartijn Dashorst
2.4K views79 slides
Solutions for when documentation fails by
Solutions for when documentation fails Solutions for when documentation fails
Solutions for when documentation fails Martijn Dashorst
933 views123 slides
Whats up with wicket 8 and java 8 by
Whats up with wicket 8 and java 8Whats up with wicket 8 and java 8
Whats up with wicket 8 and java 8Martijn Dashorst
491 views108 slides
Code review drinking game by
Code review drinking gameCode review drinking game
Code review drinking gameMartijn Dashorst
1.1K views14 slides

More from Martijn Dashorst(20)

Converting 85% of Dutch Primary Schools from Oracle to PostgreSQL by Martijn Dashorst
Converting 85% of Dutch Primary Schools from Oracle to PostgreSQLConverting 85% of Dutch Primary Schools from Oracle to PostgreSQL
Converting 85% of Dutch Primary Schools from Oracle to PostgreSQL
Martijn Dashorst2.4K views
Solutions for when documentation fails by Martijn Dashorst
Solutions for when documentation fails Solutions for when documentation fails
Solutions for when documentation fails
Martijn Dashorst933 views
Who Automates the Automators? (Quis Automatiet Ipsos Automates?) by Martijn Dashorst
Who Automates the Automators? (Quis Automatiet Ipsos Automates?)Who Automates the Automators? (Quis Automatiet Ipsos Automates?)
Who Automates the Automators? (Quis Automatiet Ipsos Automates?)
Martijn Dashorst606 views
Apache Wicket and Java EE sitting in a tree by Martijn Dashorst
Apache Wicket and Java EE sitting in a treeApache Wicket and Java EE sitting in a tree
Apache Wicket and Java EE sitting in a tree
Martijn Dashorst2.1K views
Keep your Wicket application in production by Martijn Dashorst
Keep your Wicket application in productionKeep your Wicket application in production
Keep your Wicket application in production
Martijn Dashorst3.5K views
Guide To Successful Graduation at Apache by Martijn Dashorst
Guide To Successful Graduation at ApacheGuide To Successful Graduation at Apache
Guide To Successful Graduation at Apache
Martijn Dashorst534 views

Recently uploaded

tecnologia18.docx by
tecnologia18.docxtecnologia18.docx
tecnologia18.docxnosi6702
5 views5 slides
Benefits in Software Development by
Benefits in Software DevelopmentBenefits in Software Development
Benefits in Software DevelopmentJohn Valentino
5 views15 slides
Gen Apps on Google Cloud PaLM2 and Codey APIs in Action by
Gen Apps on Google Cloud PaLM2 and Codey APIs in ActionGen Apps on Google Cloud PaLM2 and Codey APIs in Action
Gen Apps on Google Cloud PaLM2 and Codey APIs in ActionMárton Kodok
15 views55 slides
Electronic AWB - Electronic Air Waybill by
Electronic AWB - Electronic Air Waybill Electronic AWB - Electronic Air Waybill
Electronic AWB - Electronic Air Waybill Freightoscope
5 views1 slide
Keep by
KeepKeep
KeepGeniusee
78 views10 slides
DRYiCE™ iAutomate: AI-enhanced Intelligent Runbook Automation by
DRYiCE™ iAutomate: AI-enhanced Intelligent Runbook AutomationDRYiCE™ iAutomate: AI-enhanced Intelligent Runbook Automation
DRYiCE™ iAutomate: AI-enhanced Intelligent Runbook AutomationHCLSoftware
6 views8 slides

Recently uploaded(20)

tecnologia18.docx by nosi6702
tecnologia18.docxtecnologia18.docx
tecnologia18.docx
nosi67025 views
Gen Apps on Google Cloud PaLM2 and Codey APIs in Action by Márton Kodok
Gen Apps on Google Cloud PaLM2 and Codey APIs in ActionGen Apps on Google Cloud PaLM2 and Codey APIs in Action
Gen Apps on Google Cloud PaLM2 and Codey APIs in Action
Márton Kodok15 views
Electronic AWB - Electronic Air Waybill by Freightoscope
Electronic AWB - Electronic Air Waybill Electronic AWB - Electronic Air Waybill
Electronic AWB - Electronic Air Waybill
Freightoscope 5 views
DRYiCE™ iAutomate: AI-enhanced Intelligent Runbook Automation by HCLSoftware
DRYiCE™ iAutomate: AI-enhanced Intelligent Runbook AutomationDRYiCE™ iAutomate: AI-enhanced Intelligent Runbook Automation
DRYiCE™ iAutomate: AI-enhanced Intelligent Runbook Automation
HCLSoftware6 views
FIMA 2023 Neo4j & FS - Entity Resolution.pptx by Neo4j
FIMA 2023 Neo4j & FS - Entity Resolution.pptxFIMA 2023 Neo4j & FS - Entity Resolution.pptx
FIMA 2023 Neo4j & FS - Entity Resolution.pptx
Neo4j17 views
AI and Ml presentation .pptx by FayazAli87
AI and Ml presentation .pptxAI and Ml presentation .pptx
AI and Ml presentation .pptx
FayazAli8713 views
FOSSLight Community Day 2023-11-30 by Shane Coughlan
FOSSLight Community Day 2023-11-30FOSSLight Community Day 2023-11-30
FOSSLight Community Day 2023-11-30
Shane Coughlan6 views
Ports-and-Adapters Architecture for Embedded HMI by Burkhard Stubert
Ports-and-Adapters Architecture for Embedded HMIPorts-and-Adapters Architecture for Embedded HMI
Ports-and-Adapters Architecture for Embedded HMI
Burkhard Stubert26 views
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with... by sparkfabrik
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...
sparkfabrik8 views
Fleet Management Software in India by Fleetable
Fleet Management Software in India Fleet Management Software in India
Fleet Management Software in India
Fleetable12 views
Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated... by TomHalpin9
Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated...Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated...
Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated...
TomHalpin96 views
Unlocking the Power of AI in Product Management - A Comprehensive Guide for P... by NimaTorabi2
Unlocking the Power of AI in Product Management - A Comprehensive Guide for P...Unlocking the Power of AI in Product Management - A Comprehensive Guide for P...
Unlocking the Power of AI in Product Management - A Comprehensive Guide for P...
NimaTorabi215 views
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx by animuscrm
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx
animuscrm15 views

HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0

  • 1. HTMX: WEB 1.0 WITH WEB 2.0 BENEFITS WITHOUT THE GRIFT OF WEB 3.0 MARTIJN DASHORST # jfall.
  • 2. WHO HAS HEARD OF HTMX BEFORE THIS TALK?
  • 3. WHO HAS HEARD OF INTERCOOLER TURBOLINKS, OR HOTWIRE?
  • 4. HTMX is an old idea that's getting a lot of new attention — @bholmesdev
  • 11. HERE WE ARE I THOUGHT IT WAS A GOOD IDEA TO PITCH A PRESENTATION FOR JFALL ABOUT HTMX TO GET TO KNOW IT BETTER. SO...
  • 12. MARTIJN DASHORST ● 18 YEARS @TOPICUS PARNASSYS, PARRO, SOMTODAY, EDUARTE SPREEKUUR.NL, DARMKANKER SCREENING, CORONATEST.NL, FORCE (MORTGAGES) ● BACKEND DEVELOPER BY HEART ● LOVES CREATING FIXING LEGACY SOFTWARE ● CONTRIBUTOR APACHE WICKET ● CO-AUTHORED WICKET IN ACTION
  • 13. WHY HTMX? HTMX = WEB 1.0 + WEB 2.0 - WEB 3.0 01 04 02 05 03 WHAT IS HTMX? HOW DOES IT WORK? DEMOS CONCLUSIONS
  • 14. HTMX https://htmx.org intercooler 2013-2020 14kb JavaScript library (gzipped) - no dependencies - no build - attributes - css classes - request/response headers - events - extensions - javascript API
  • 15. HTMX https://htmx.org version 1.9.8 (Nov 6, 2023) 14kb JavaScript library (gzipped) - no dependencies - no build - attributes - css classes - request/response headers - events - extensions - javascript API <script src="htmx.js"></script>
  • 16. HTMX https://htmx.org version 1.9.8 (Nov 6, 2023) 14kb JavaScript library (gzipped) - no dependencies - no build - attributes - css classes - request/response headers - events - extensions - javascript API <script src="htmx.js"></script> <button hx-get="/counter" hx-swap="outerHTML"> Click me</button>
  • 17. HTMX https://htmx.org version 1.9.8 (Nov 6, 2023) 14kb JavaScript library (gzipped) - no dependencies - no build - attributes - css classes - request/response headers - events - extensions - javascript API <script src="htmx.js"></script> <button hx-get="/counter" hx-swap="outerHTML" hx-indicator="#indicator" >Click me</button> <i id="indicator" class="htmx-indicator spinner-border"> </i>
  • 18. By Carson Gross https://bigsky.software A Return to Hypermedia – "Curing Your JavaScript Fatigue Using The Original Architecture Of The Web" htmx: Writing JavaScript to Avoid Writing JavaScript HTMx: Building modern web applications without JS HTMX
  • 19. one day code base understandable and grug can get work done, everything good! next day impossible: complexity demon spirit has entered code and very dangerous situation! — https://grugbrain.dev/
  • 20. WHY HTMX? HTMX = WEB 1.0 + WEB 2.0 - WEB 3.0 01 04 02 05 03 WHAT IS HTMX? HOW DOES IT WORK? DEMOS CONCLUSIONS
  • 21. BUILDING A REST API FOR A WEB PORTAL AND NATIVE APP MY EXPERIENCES REST server xml html xml
  • 22. BUILDING A REST API FOR A WEB PORTAL MY EXPERIENCES
  • 23. BUILDING A REST API FOR A WEB PORTAL MY EXPERIENCES data format for API: JSON or XML
  • 24. BUILDING A REST API FOR A WEB PORTAL MY EXPERIENCES data format for API: JSON or XML POST or PUT?
  • 25. BUILDING A REST API FOR A WEB PORTAL MY EXPERIENCES data format for API: JSON or XML caching the resources POST or PUT?
  • 26. BUILDING A REST API FOR A WEB PORTAL MY EXPERIENCES data format for API: JSON or XML caching the resources paging of results POST or PUT?
  • 27. BUILDING A REST API FOR A WEB PORTAL MY EXPERIENCES data format for API: JSON or XML versioning the API caching the resources paging of results POST or PUT?
  • 28. BUILDING A REST API FOR A WEB PORTAL MY EXPERIENCES data format for API: JSON or XML versioning the API caching the resources paging of results POST or PUT? POST to /resource or / resource/new ?
  • 29. BUILDING A REST API FOR A WEB PORTAL MY EXPERIENCES data format for API: JSON or XML versioning the API caching the resources paging of results POST or PUT? POST to /resource or / resource/new ? Links between resources?
  • 30. BUILDING A REST API FOR A WEB PORTAL MY EXPERIENCES data format for API: JSON or XML versioning the API caching the resources paging of results POST or PUT? POST to /resource or / resource/new ? Links between resources? Complex UIs, more generic REST resources, God Objects
  • 34. Dear Roy, What is the best practice for versioning a REST API?
  • 35. DON'T — Roy T. Fielding Dear Roy, What is the best practice for versioning a REST API? https:/ /www.slideshare.net/evolve_conference/201308-fielding-evolve#31
  • 36. Dear Roy, What is the best practice for versioning a REST API? https:/ /www.slideshare.net/evolve_conference/201308-fielding-evolve#31 when you use hypertext as the engine of application state, you don't need it — Roy T. Fielding
  • 37. SPA architecture = JSON + RPC "REST"
  • 38. SPA architecture = JSON + RPC "REST" WHY?
  • 39. WHY CAN ONLY <A> AND <FORM> PERFORM THOSE REQUESTS? WHY ONLY GET & POST REQUESTS?
  • 42. WHY HTMX? HTMX = WEB 1.0 + WEB 2.0 - WEB 3.0 01 04 02 05 03 WHAT IS HTMX? HOW DOES IT WORK? DEMOS CONCLUSIONS
  • 43. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component
  • 44. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component credit: @htmx.org@twitter https:/ /twitter.com/htmx_org/status/1700259958405869711
  • 45. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component <button class="btn btn-success" onclick=" const req = new XMLHttpRequest(); req.onload = (e) => { const admin = document.getElementById('admin'); const newAdmin = req.responseXML.getElementById('admin'); admin.innerHTML = newAdmin.innerHTML; }; req.responseType = 'document'; req.open('GET', '/admin'); req.overrideMimeType('text/html'); req.send();" >Click me</button> <section id="admin"></section>
  • 46. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component <button class="btn btn-primary" hx-get="/admin" hx-select="#admin" hx-target="#admin" >Click me</button> <section id="admin"></section>
  • 47. GETTING THE SCRIPT <script src="https://unpkg.com/htmx.org@1.9.7"></script> Quick and dirty
  • 48. TIP: USE WEBJARS i Using webjars <dependency> <groupId>org.webjars.npm</groupId> <artifactId>htmx.org</artifactId> <version>1.9.7</version> </dependency> <script src="/webjars/htmx.org/1.9.7/dist/htmx.js"></script> /META-INF/resources/webjars «classpath» SEE ALSO: https:/ /www.baeldung.com/maven-webjars
  • 49. TIP: WEBJARS + LOCATOR i Spring <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> <version>0.48</version> </dependency> <script src="/webjars/htmx.org/dist/htmx.js"></script> no version Quarkus quarkus ext add io.quarkus:quarkus-webjars-locator SEE ALSO: https:/ /www.baeldung.com/maven-webjars
  • 50. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component Attributes instruct HTMX what to do with what element
  • 51. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component Attributes instruct HTMX what to do with what element hx-boost="true|false" hx-get="/count" hx-post="/count" hx-target="#counter" hx-swap="innerHTML|outerHTML|afterend|..." hx-trigger="click|load|every 1s" hx-select="#htmx-badge" hx-swap-oob="true|outerHTML:selector" hx-delete|patch|put="/url" ...
  • 52. ATTRIBUTES – hx-boost <nav hx-boost="true"> <li><a href="/menu1">Menu 1</a></li> <li><a href="/menu2">Menu 2</a></li> <li><a href="/menu3">Menu 3</a></li> <li><a href="/menu4">Menu 4</a></li> </nav> Replaces the GET/POST request using AJAX and replaces the body using innerHTML swap. inherited
  • 53. ATTRIBUTES – hx-get <button hx-get="/speaker/1">Get speaker 1</button> Issue a GET request to the specified URL and swap the returned HTML into the DOM using a swap strategy
  • 54. ATTRIBUTES – hx-get <button hx-get="/speaker/1">Get speaker 1</button> Issue a GET request to the specified URL and swap the returned HTML into the DOM using a swap strategy
  • 55. ATTRIBUTES – hx-get <button hx-get="/speaker/1">Get speaker 1</button> Issue a GET request to the specified URL and swap the returned HTML into the DOM using a swap strategy <div id="speaker">...</div>
  • 56. ATTRIBUTES – hx-get <button hx-get="/speaker/1">Get speaker 1</button> Issue a GET request to the specified URL and swap the returned HTML into the DOM using a swap strategy <div id="speaker">...</div> <button hx-get="/speaker/1"> Get speaker 1 </button>
  • 57. ATTRIBUTES – hx-get <button hx-get="/speaker/1">Get speaker 1</button> Issue a GET request to the specified URL and swap the returned HTML into the DOM using a swap strategy <div id="speaker">...</div> <button hx-get="/speaker/1"> <div id="speaker">...</div> </button>
  • 58. ATTRIBUTES – hx-swap <button hx-get="/speaker/1" hx-swap="outerHTML">Get speaker 1</button> Controls how the HTML is swapped into the DOM. innerHTML, outerHTML, beforebegin, afterbegin, beforeend, afterend, delete, none <div id="speaker">...</div> <button hx-get="/speaker/1" hx-swap="outerHTML">Get speaker 1</button> inherited
  • 59. ATTRIBUTES – hx-swap <button hx-get="/speaker/1" hx-swap="outerHTML">Get speaker 1</button> Controls how the HTML is swapped into the DOM. innerHTML, outerHTML, beforebegin, afterbegin, beforeend, afterend, delete, none <div id="speaker">...</div> <div id="speaker">...</div>
  • 60. ATTRIBUTES – hx-target Controls which element in the DOM gets swapped with the returned HTML using a swapping strategy. inherited <button hx-get="/speaker/1" hx-swap="outerHTML" hx-target="#speaker">Get speaker 1</button> <div id="speaker">No content</div>
  • 61. ATTRIBUTES – hx-target Controls which element in the DOM gets swapped with the returned HTML using a swapping strategy. inherited <button hx-get="/speaker/1" hx-swap="outerHTML" hx-target="#speaker">Get speaker 1</button> <div id="speaker">No content</div> <div id="speaker">Johan Janssen</div> <div id="speaker">No content</div>
  • 62. ATTRIBUTES – hx-target Controls which element in the DOM gets swapped with the returned HTML using a swapping strategy. inherited <button hx-get="/speaker/1" hx-swap="outerHTML" hx-target="#speaker">Get speaker 1</button> <div id="speaker">No content</div> <div id="speaker" class="card">Johan Janssen</div> <div id="speaker">No content</div>
  • 63. ATTRIBUTES – hx-target Controls which element in the DOM gets swapped with the returned HTML using a swapping strategy. inherited <button hx-get="/speaker/1" hx-swap="outerHTML" hx-target="#speaker">Get speaker 1</button> <div id="speaker">No content</div> <div id="speaker" class="card">Johan Janssen</div> <div id="speaker" class="card">Johan Janssen</div>
  • 64. ATTRIBUTES – hx-target Inherited??? inherited <menu> <a hx-get="/speaker/1" hx-target="#speaker">Get speaker 1 <a hx-get="/speaker/2" hx-target="#speaker">Get speaker 2 <a hx-get="/speaker/3" hx-target="#speaker">Get speaker 3 <a hx-get="/speaker/4" hx-target="#speaker">Get speaker 4 </menu>
  • 65. ATTRIBUTES – hx-target hx-target is inherited and can be placed on a parent element. inherited <menu hx-target="#speaker"> <a hx-get="/speaker/1">Get speaker 1</a> <a hx-get="/speaker/2">Get speaker 2</a> <a hx-get="/speaker/3">Get speaker 3</a> <a hx-get="/speaker/4">Get speaker 4</a> </menu>
  • 66. MORE ABOUT ATTRIBUTES INHERITED ATTRIBUTES NO JAVASCRIPT REQ'D Removes repetition of common attributes, such as the target, swap method, selecting elements etc. Can be overridden by hx-disinherit With just the tags you can create rich dynamically updating pages. Slap on some attributes and continue. In the HTML response you can add more updates by using out-of-band swaps (hx-swap-oob) UPDATE MULTIPLE ELEMENTS
  • 67. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component Attributes instruct HTMX what to do with what element Server may need to distinguish between normal and HTMX request: Headers
  • 68. HX-Request always true HX-Boosted indicates hx-boost request HX-Current-URL ... ... REQUEST HEADERS RESPONSE HEADERS HX-Redirect client-side redirect HX-Refresh client-side full refresh of the page HX-Retarget modify the hx-target HX-Trigger trigger client-side events ... HOW DOES IT WORK – HEADERS Convenient for deciding to return a partial or a full page DETECT HTMX REQUEST MODIFY REQUESTS Change the target, or swapping mechanism, send a redirect or replace the URL in the location bar. And more.
  • 69. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component Attributes instruct HTMX what to do with what element Server may need to distinguish between normal and HTMX request: Headers When a HTMX request is in flight, feedback is wanted for Users: classes
  • 70. htmx-indicator toggles visibility (opacity:1) htmx-request applied to hx-indicator during request htmx-added htmx-swapping htmx-settling CLASSES HOW DOES IT WORK – CLASSES Convenient for deciding to return a partial or a full page INDICATOR ANIMATIONS Animate swapping of elements for a more rich experience.
  • 71. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component Attributes instruct HTMX what to do with what element Server may need to distinguish between normal and HTMX request: Headers When a HTMX request is in flight, feedback is wanted for Users: classes Complex needs for reacting to interactions, swapping, requests & responses: events
  • 72. HOW DOES IT WORK? – EVENTS htmx:abort htmx:afterOnLoad htmx:afterProcessNode htmx:afterRequest htmx:afterSettle htmx:afterSwap htmx:beforeCleanupElement htmx:beforeOnLoad htmx:beforeProcessNode htmx:beforeRequest htmx:beforeSwap htmx:beforeSend htmx:configRequest htmx:confirm htmx:historyCacheError htmx:historyCacheMiss htmx:historyCacheMissError htmx:historyCacheMissLoad htmx:historyRestore htmx:beforeHistorySave htmx:load htmx:noSSESourceError htmx:onLoadError htmx:oobAfterSwap htmx:oobBeforeSwap htmx:oobErrorNoTarget htmx:prompt htmx:pushedIntoHistory htmx:responseError htmx:sendError htmx:sseError htmx:sseOpen htmx:swapError htmx:targetError htmx:timeout htmx:validation:validate htmx:validation:failed htmx:validation:halted htmx:xhr:abort htmx:xhr:loadend htmx:xhr:loadstart htmx:xhr:progress
  • 73. HOW DOES IT WORK? – EVENTS before/after swaps occurred, after DOM has settled, out-of-bounds swaps, etc. Anything relevant to modifying using the history API The lifecycle of the XHR request: progress, loadend, loadstart, etc. Errors sending a request, receiving a response, targetting an element, etc. SWAPPING HISTORY VALIDATION Triggered when client side validation is performed, failed. Configure the request before it is sent to the server, response processing XHR ERRORS REQUESTS
  • 74. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component Attributes instruct HTMX what to do with what element Server may need to distinguish between normal and HTMX request: Headers When a HTMX request is in flight, feedback is wanted for Users: classes Complex needs for reacting to interactions, swapping, requests & responses: events In the events some annoying JavaScript needs to be executed (e.g. add class): JavaScript library
  • 75. FINDING ELEMENTS HOW DOES IT WORK? – JAVASCRIPT addClass(), removeClass(), toggleClass(), takeClass() remove(), values() EVENTS ON ELEMENTS on(), off(), trigger() closest(), find(), findAll() config, ajax(), process() defineExtension(), removeExtension() WORKING WITH CLASSES WORKING WITH ELEMENTS INTERNALS & EXTENSIONS
  • 76. HOW DOES IT WORK? – JAVASCRIPT Documents Projects Members Admin Gangplank lookout killick jack yo-ho-ho Sea Legs yard marooned interloper yawl. Gabion fire ship Brethren of the Coast lanyard chase Cat o'nine tails dead men tell no tales barque yawl Nelsons folly. <ul class="list-group" hx-target="#content" hx-on::after-request="htmx.takeClass(event.detail.elt, 'active')"> <li class="list-group-item" hx-get="/sub/Documents">Documents</li> <li class="list-group-item" hx-get="/sub/Projects">Projects</li> <li class="list-group-item" hx-get="/sub/Members">Members</li> <li class="list-group-item" hx-get="/sub/Admin">Admin</li> </ul> <section id="content"></section>
  • 77. HOW DOES IT WORK? Any HTML element can interact as an hypermedia component Attributes instruct HTMX what to do with what element Server may need to distinguish between normal and HTMX request: Headers When a HTMX request is in flight, feedback is wanted for Users: classes Complex needs for reacting to interactions, swapping, requests & responses: events In the events some annoying JavaScript needs to be executed (e.g. add class): JavaScript library We need to use a different HTML morphing algo, serverside sent events or web sockets: extensions
  • 78. FINALLY HATEOAS? HTMX = WEB 1.0 + WEB 2.0 - WEB 3.0 01 04 02 05 03 WHAT IS HTMX? HOW DOES IT WORK? DEMOS CONCLUSIONS
  • 81. fun with flags ● quarkus ● qute ● websockets ● htmx ● htmx-ws ● bootstrap ● flag=icons github.com/dashorst/funwithflags
  • 84. WEBSOCKETS FTW! @ServerEndpoint("/game/{player}") public class FunWithFlagsWebSocket { @OnOpen public void onOpen(Session session, @PathParam("player") String encodedPlayer) { var player = URLDecoder.decode(encodedPlayer, StandardCharsets.UTF_8); sessions.put(player, session); funWithFlagsGame.registerPlayer(session, player); }
  • 85. WEBSOCKETS FTW! @ServerEndpoint("/game/{player}") public class FunWithFlagsWebSocket { @OnOpen public void onOpen(Session session, @PathParam("player") String encodedPlayer) { var player = URLDecoder.decode(encodedPlayer, StandardCharsets.UTF_8); sessions.put(player, session); funWithFlagsGame.registerPlayer(session, player); } <div id="game" hx-ext="ws" ws-connect="/game/{playername}" class="row"> <div class="col-12 text-center"> <h3>Joining lobby...</h3> {#fragment id=lobby} <ul class="list-group" id="lobby-list"> {#for player in players} <li class="list-group-item {#if player.name == playername} list-group- {/for} </ul> {/fragment} <p>Waiting for other players to join!</p> </div> </div>
  • 86. WEBSOCKETS FTW! @ServerEndpoint("/game/{player}") public class FunWithFlagsWebSocket { @OnOpen public void onOpen(Session session, @PathParam("player") String encodedPlayer) { var player = URLDecoder.decode(encodedPlayer, StandardCharsets.UTF_8); sessions.put(player, session); funWithFlagsGame.registerPlayer(session, player); } <div id="game" hx-ext="ws" ws-connect="/game/{playername}" class="row"> <div class="col-12 text-center"> <h3>Joining lobby...</h3> {#fragment id=lobby} <ul class="list-group" id="lobby-list"> {#for player in players} <li class="list-group-item {#if player.name == playername} list-group- {/for} </ul> {/fragment} <p>Waiting for other players to join!</p> </div> </div> hx-ext="ws" ws-connect="/game/{playername}" @ServerEndpoint("/game/{player}") @OnOpen public void onOpen(Session session, @PathParam("player")
  • 87. SENDING TURN START TO PLAYERS public void onTurnStarted(@Observes TurnStarted event) { var game = event.game(); var turn = event.turn(); var countryToGuess = turn.countryToGuess(); for (Player player : game.players()) { var turnHtml = Templates.game$turnPartial(player, game, turn, countryToGuess) .render(); var rankHtml = Templates.game$rankingPartial(player, game, turn).render(); player.session().getAsyncRemote().sendObject(turnHtml + "n" + rankHtml); } } <div id="turn" class="col-12 col-lg-6 offset-lg-3"> <h2>Round {game.turnNumber} of {game.numberOfTurns}</h2> ... </div> <div id="in-game-ranking" class="col-12 col-md-3"> <h3>Ranking round {game.turnNumber}</h3> ... </div>
  • 88. SENDING TURN START TO PLAYERS public void onTurnStarted(@Observes TurnStarted event) { var game = event.game(); var turn = event.turn(); var countryToGuess = turn.countryToGuess(); for (Player player : game.players()) { var turnHtml = Templates.game$turnPartial(player, game, turn, countryToGuess) .render(); var rankHtml = Templates.game$rankingPartial(player, game, turn).render(); player.session().getAsyncRemote().sendObject(turnHtml + "n" + rankHtml); } } <div id="turn" class="col-12 col-lg-6 offset-lg-3"> <h2>Round {game.turnNumber} of {game.numberOfTurns}</h2> ... </div> <div id="in-game-ranking" class="col-12 col-md-3"> <h3>Ranking round {game.turnNumber}</h3> ... </div>
  • 89. SENDING TURN START TO PLAYERS public void onTurnStarted(@Observes TurnStarted event) { var game = event.game(); var turn = event.turn(); var countryToGuess = turn.countryToGuess(); for (Player player : game.players()) { var turnHtml = Templates.game$turnPartial(player, game, turn, countryToGuess) .render(); var rankHtml = Templates.game$rankingPartial(player, game, turn).render(); player.session().getAsyncRemote().sendObject(turnHtml + "n" + rankHtml); } } <div id="turn" class="col-12 col-lg-6 offset-lg-3"> <h2>Round {game.turnNumber} of {game.numberOfTurns}</h2> ... </div> <div id="in-game-ranking" class="col-12 col-md-3"> <h3>Ranking round {game.turnNumber}</h3> ... </div>
  • 90. SENDING TURN START TO PLAYERS public void onTurnStarted(@Observes TurnStarted event) { var game = event.game(); var turn = event.turn(); var countryToGuess = turn.countryToGuess(); for (Player player : game.players()) { var turnHtml = Templates.game$turnPartial(player, game, turn, countryToGuess) .render(); var rankHtml = Templates.game$rankingPartial(player, game, turn).render(); player.session().getAsyncRemote().sendObject(turnHtml + "n" + rankHtml); } } <div id="turn" class="col-12 col-lg-6 offset-lg-3"> <h2>Round {game.turnNumber} of {game.numberOfTurns}</h2> ... </div> <div id="in-game-ranking" class="col-12 col-md-3"> <h3>Ranking round {game.turnNumber}</h3> ... </div>
  • 91. SENDING TURN START TO PLAYERS public void onTurnStarted(@Observes TurnStarted event) { var game = event.game(); var turn = event.turn(); var countryToGuess = turn.countryToGuess(); for (Player player : game.players()) { var turnHtml = Templates.game$turnPartial(player, game, turn, countryToGuess) .render(); var rankHtml = Templates.game$rankingPartial(player, game, turn).render(); player.session().getAsyncRemote().sendObject(turnHtml + "n" + rankHtml); } } <div id="turn" class="col-12 col-lg-6 offset-lg-3"> <h2>Round {game.turnNumber} of {game.numberOfTurns}</h2> ... </div> <div id="in-game-ranking" class="col-12 col-md-3"> <h3>Ranking round {game.turnNumber}</h3> ... </div>
  • 92. DEMO
  • 93. FINALLY HATEOAS? HTMX = WEB 1.0 + WEB 2.0 - WEB 3.0 01 04 02 05 03 WHAT IS HTMX? HOW DOES IT WORK? DEMOS CONCLUSIONS
  • 94. BACKEND DEVS REJOICE HTMX: WEB 1.0 + WEB 2.0 - WEB 3.0 No more general APIs between your user interface and your back end. Use hypermedia as the engine for your application state Hypermedia On Whatever You'd Like. Backend agnostic! Spring Boot, Quarkus, JSP, PHP, DJango, Ruby on Rails, ... HTMX allows you to build rich front ends without having to learn rich front end technologies. Just one script tag and your back end. Written in whatever you'd like. REST WITH HATEOAS HOWL STACK REDUCE THE CHURN
  • 95. It took me ~2 months spare time to write Fun with Flags, but most of that time was spent figuring out the game mechanics, not HTMX integration. 9/10, would use again1 1 (already using it in other projects)
  • 96. HTMX.ORG https://htmx.org Great documentation, examples and essays on the fundamentals of HTMX and hypermedia. WARNING Also memes
  • 97. HYPERMEDIA SYSTEMS https://hypermedia.systems Best explanation of Hypermedia, REST, RESTFUL and HATEOAS. WARNING Risk of wanting to use HTMX after reading
  • 99. CREDITS: This presentation template was created by Slidesgo, and includes icons by Flaticon, and infographics & images by Freepik THANKS! Do you have any questions? martijn.dashorst@topicus.nl @dashorst@mastodon.social martijndashorst.com CREDITS HTMX was conceived by Carson Gross Title "HTMX: WEB 1.0 WITH WEB 2.0 BENEFITS WITHOUT THE GRIFT OF WEB 3.0" was inspired by The Primagen Template by slidesgo and includes images from flaticon and icons from freepik
  • 100. Thanks for your attention Please rate my session in the J-Fall app # jfall. fun-with-flags.eduarte.dev HTMX: web 1.0 + web 2.0 - web 3.0 Martijn Dashorst topicus