SlideShare a Scribd company logo
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

More Related Content

PPTX
django part-1
PDF
GET and POST in PHP
PPTX
Git and GitHub
PDF
A Complete Guide to Python Web Development
PPTX
Css pseudo-classes
PDF
Git Started With Git
PPTX
Cascading style sheets (CSS)
PDF
Introduction to Elixir
django part-1
GET and POST in PHP
Git and GitHub
A Complete Guide to Python Web Development
Css pseudo-classes
Git Started With Git
Cascading style sheets (CSS)
Introduction to Elixir

What's hot (20)

PPTX
Go micro framework to build microservices
PPTX
Как писать красивый код или основы SOLID
PDF
CSS media types
PPTX
Event In JavaScript
PDF
Pengenalan Git
PDF
Dv Pmysqluc Federation At Flickr Doing Billions Of Queries Per Day
PPT
PPTX
Grokking opensource with github
PPTX
Javascript event handler
PDF
Loops in JavaScript
KEY
The everyday developer's guide to version control with Git
PDF
Lecture-3: Introduction to html - Basic Structure & Block Building
PPTX
Solid principles
PPTX
Css tables
PPT
Introduction to Cascading Style Sheets (CSS)
PDF
Python Advanced – Building on the foundation
PPTX
Jquery Complete Presentation along with Javascript Basics
PPTX
File Uploading in PHP
PPTX
Html5 and-css3-overview
PPTX
Git in 10 minutes
Go micro framework to build microservices
Как писать красивый код или основы SOLID
CSS media types
Event In JavaScript
Pengenalan Git
Dv Pmysqluc Federation At Flickr Doing Billions Of Queries Per Day
Grokking opensource with github
Javascript event handler
Loops in JavaScript
The everyday developer's guide to version control with Git
Lecture-3: Introduction to html - Basic Structure & Block Building
Solid principles
Css tables
Introduction to Cascading Style Sheets (CSS)
Python Advanced – Building on the foundation
Jquery Complete Presentation along with Javascript Basics
File Uploading in PHP
Html5 and-css3-overview
Git in 10 minutes
Ad

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

PPTX
HTTP/2 : why upgrading the web? - DjangoCon Europe 2016 Budapest
PPTX
HTTP/2 : why upgrading the web? - apidays Paris
PDF
Fulfilling the Hypermedia Constraint via HTTP OPTIONS, The HTTP Vocabulary In...
PPTX
Http2: why the web is upgrading? - bdx.io 2015
PDF
POX to HATEOAS: Our Company's Journey Building a Hypermedia API
PPTX
HTTP/2
PPTX
The end of polling : why and how to transform a REST API into a Data Streamin...
PDF
HTTP Basics Demo
PPT
Introduction to python scrapping
PPT
Hanoi php day 2008 - 05. nguyen hai nhat huy - building-restful-web-service-w...
ZIP
The Power of Open Data
PDF
Together Cheerfully to Walk with Hypermedia
PDF
HTTP colon slash slash: end of the road? @ CakeFest 2013 in San Francisco
PPTX
ASP.NET WEB API Training
PPT
KMUTNB - Internet Programming 2/7
PPTX
From ZERO to REST in an hour
PPTX
11 asp.net web api
PDF
Talking to Web Services
PDF
URL Design
PPTX
ASP.NET Web API and HTTP Fundamentals
HTTP/2 : why upgrading the web? - DjangoCon Europe 2016 Budapest
HTTP/2 : why upgrading the web? - apidays Paris
Fulfilling the Hypermedia Constraint via HTTP OPTIONS, The HTTP Vocabulary In...
Http2: why the web is upgrading? - bdx.io 2015
POX to HATEOAS: Our Company's Journey Building a Hypermedia API
HTTP/2
The end of polling : why and how to transform a REST API into a Data Streamin...
HTTP Basics Demo
Introduction to python scrapping
Hanoi php day 2008 - 05. nguyen hai nhat huy - building-restful-web-service-w...
The Power of Open Data
Together Cheerfully to Walk with Hypermedia
HTTP colon slash slash: end of the road? @ CakeFest 2013 in San Francisco
ASP.NET WEB API Training
KMUTNB - Internet Programming 2/7
From ZERO to REST in an hour
11 asp.net web api
Talking to Web Services
URL Design
ASP.NET Web API and HTTP Fundamentals
Ad

More from Martijn Dashorst (20)

PDF
From Floppy Disks to Cloud Deployments
PDF
SOLID principles
PDF
Converting 85% of Dutch Primary Schools from Oracle to PostgreSQL
PDF
Solutions for when documentation fails
PDF
Whats up with wicket 8 and java 8
PDF
Code review drinking game
PDF
Java Serialization Deep Dive
PDF
Code review drinking game
PDF
Scrum: van praktijk naar onderwijs
PDF
Who Automates the Automators? (Quis Automatiet Ipsos Automates?)
PDF
De schone coder
PDF
Wicket 10 years and beyond
PDF
Apache Wicket and Java EE sitting in a tree
PDF
The State of Wicket
KEY
Wicket 2010
PDF
Vakmanschap is meesterschap
PDF
Keep your Wicket application in production
PDF
Wicket In Action - oredev2008
PDF
Guide To Successful Graduation at Apache
PDF
Wicket In Action
From Floppy Disks to Cloud Deployments
SOLID principles
Converting 85% of Dutch Primary Schools from Oracle to PostgreSQL
Solutions for when documentation fails
Whats up with wicket 8 and java 8
Code review drinking game
Java Serialization Deep Dive
Code review drinking game
Scrum: van praktijk naar onderwijs
Who Automates the Automators? (Quis Automatiet Ipsos Automates?)
De schone coder
Wicket 10 years and beyond
Apache Wicket and Java EE sitting in a tree
The State of Wicket
Wicket 2010
Vakmanschap is meesterschap
Keep your Wicket application in production
Wicket In Action - oredev2008
Guide To Successful Graduation at Apache
Wicket In Action

Recently uploaded (20)

PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
Tally Prime Crack Download New Version 5.1 [2025] (License Key Free
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
Cost to Outsource Software Development in 2025
PPTX
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
PDF
medical staffing services at VALiNTRY
PDF
Download FL Studio Crack Latest version 2025 ?
PDF
Salesforce Agentforce AI Implementation.pdf
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PPTX
Why Generative AI is the Future of Content, Code & Creativity?
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
iTop VPN 6.5.0 Crack + License Key 2025 (Premium Version)
PPTX
Log360_SIEM_Solutions Overview PPT_Feb 2020.pptx
PPTX
history of c programming in notes for students .pptx
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Operating system designcfffgfgggggggvggggggggg
Tally Prime Crack Download New Version 5.1 [2025] (License Key Free
Navsoft: AI-Powered Business Solutions & Custom Software Development
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Cost to Outsource Software Development in 2025
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
medical staffing services at VALiNTRY
Download FL Studio Crack Latest version 2025 ?
Salesforce Agentforce AI Implementation.pdf
Odoo Companies in India – Driving Business Transformation.pdf
wealthsignaloriginal-com-DS-text-... (1).pdf
Why Generative AI is the Future of Content, Code & Creativity?
Wondershare Filmora 15 Crack With Activation Key [2025
Internet Downloader Manager (IDM) Crack 6.42 Build 41
How to Choose the Right IT Partner for Your Business in Malaysia
Design an Analysis of Algorithms II-SECS-1021-03
iTop VPN 6.5.0 Crack + License Key 2025 (Premium Version)
Log360_SIEM_Solutions Overview PPT_Feb 2020.pptx
history of c programming in notes for students .pptx
Adobe Illustrator 28.6 Crack My Vision of Vector Design

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/[email protected]"></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? [email protected] @[email protected] 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