SlideShare a Scribd company logo
1 of 100
Download to read offline
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
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
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
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

What's hot

Introduction to Redis
Introduction to RedisIntroduction to Redis
Introduction to Redis
TO THE NEW | Technology
 

What's hot (20)

Basic of PHP
Basic of PHPBasic of PHP
Basic of PHP
 
Scaling up task processing with Celery
Scaling up task processing with CeleryScaling up task processing with Celery
Scaling up task processing with Celery
 
JavaScript
JavaScriptJavaScript
JavaScript
 
Introduction to ReactJS
Introduction to ReactJSIntroduction to ReactJS
Introduction to ReactJS
 
C++17 std::byte
C++17 std::byteC++17 std::byte
C++17 std::byte
 
Introduction to Redis
Introduction to RedisIntroduction to Redis
Introduction to Redis
 
Tomcat
TomcatTomcat
Tomcat
 
Rest web services
Rest web servicesRest web services
Rest web services
 
200.마이크로서비스에 적합한 오픈소스 WAS는 무엇?
200.마이크로서비스에 적합한 오픈소스 WAS는 무엇?200.마이크로서비스에 적합한 오픈소스 WAS는 무엇?
200.마이크로서비스에 적합한 오픈소스 WAS는 무엇?
 
Rest in flask
Rest in flaskRest in flask
Rest in flask
 
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with ExceptionGCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
 
What is JavaScript? Edureka
What is JavaScript? EdurekaWhat is JavaScript? Edureka
What is JavaScript? Edureka
 
Inside MongoDB: the Internals of an Open-Source Database
Inside MongoDB: the Internals of an Open-Source DatabaseInside MongoDB: the Internals of an Open-Source Database
Inside MongoDB: the Internals of an Open-Source Database
 
Apache tomcat
Apache tomcatApache tomcat
Apache tomcat
 
Introduction to redis
Introduction to redisIntroduction to redis
Introduction to redis
 
Joomla REST API
Joomla REST APIJoomla REST API
Joomla REST API
 
Why TypeScript?
Why TypeScript?Why TypeScript?
Why TypeScript?
 
Apache Superset - open source data exploration and visualization (Conclusion ...
Apache Superset - open source data exploration and visualization (Conclusion ...Apache Superset - open source data exploration and visualization (Conclusion ...
Apache Superset - open source data exploration and visualization (Conclusion ...
 
Laravel introduction
Laravel introductionLaravel introduction
Laravel introduction
 
Php mysql
Php mysqlPhp mysql
Php mysql
 

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

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...
Hanoi php day 2008 - 05. nguyen hai nhat huy - building-restful-web-service-w...
Nguyen Duc Phu
 

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

More from Martijn Dashorst

Keep your Wicket application in production
Keep your Wicket application in productionKeep your Wicket application in production
Keep your Wicket application in production
Martijn Dashorst
 

More from Martijn Dashorst (20)

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

Recently uploaded

Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
VictoriaMetrics
 

Recently uploaded (20)

WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
 
WSO2Con2024 - Facilitating Broadband Switching Services for UK Telecoms Provi...
WSO2Con2024 - Facilitating Broadband Switching Services for UK Telecoms Provi...WSO2Con2024 - Facilitating Broadband Switching Services for UK Telecoms Provi...
WSO2Con2024 - Facilitating Broadband Switching Services for UK Telecoms Provi...
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security Program
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
WSO2Con2024 - Navigating the Digital Landscape: Transforming Healthcare with ...
WSO2Con2024 - Navigating the Digital Landscape: Transforming Healthcare with ...WSO2Con2024 - Navigating the Digital Landscape: Transforming Healthcare with ...
WSO2Con2024 - Navigating the Digital Landscape: Transforming Healthcare with ...
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
WSO2CON 2024 - Not Just Microservices: Rightsize Your Services!
WSO2CON 2024 - Not Just Microservices: Rightsize Your Services!WSO2CON 2024 - Not Just Microservices: Rightsize Your Services!
WSO2CON 2024 - Not Just Microservices: Rightsize Your Services!
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
 
WSO2Con2024 - Low-Code Integration Tooling
WSO2Con2024 - Low-Code Integration ToolingWSO2Con2024 - Low-Code Integration Tooling
WSO2Con2024 - Low-Code Integration Tooling
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - Keynote
 

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
  • 5.
  • 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
  • 33.
  • 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
  • 82.
  • 83.
  • 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