htmx & Web Components : un combo parfait pour le développement web

A presentation at DevoxxFR in April 2025 in Paris, France by Horacio Gonzalez

Slide 1

Slide 1

</> htmx 2.0 & Web Components A Perfect Match for Frontend Development @PierreZ – @LostInBrittany DEVOXX FRANCE 2025 @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 2

Slide 2

Horacio Gonzalez @LostInBrittany Espagnol Perdu en Bretagne @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 3

Slide 3

Pierre Zemb @PierreZ Ingénieur Perdu dans le front @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 4

Slide 4

Create modern user interfaces with the simplicity and power of hypertext @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 5

Slide 5

Arbitrary Limitations of HTML ● Why can only <a> and <form> make HTTP(S) requests? ● Why can only click and submit events trigger them? ● Why are only GET and POST methods available? ● Why do <a> and <form> force a full page reload? ● Why so many arbitrary constraints in HTML? @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 6

Slide 6

Goal: Interactivity in Hypertext htmx extends HTML capabilities to: ● ● ● ● Perform AJAX requests Handle CSS transitions Work with WebSockets Process server-sent events All through declarative HTML attributes @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 7

Slide 7

But, what’s the point? It sounds nice and semantic, but what’s the real benefit? @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 8

Slide 8

A Quick Look Back A time when dinosaurs like me coded with Struts @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 9

Slide 9

Remember MVC? With its page-by-page navigation @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 10

Slide 10

The Golden Age of MVC Frameworks Generating HTML views @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 11

Slide 11

2005: The Arrival of AJAX The birth of Web 2.0 @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 12

Slide 12

Web Pages Become Dynamic Apps Powered by JavaScript and jQuery @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 13

Slide 13

Shift to Single Page Applications (SPA) @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 14

Slide 14

Increasing Complexity The rise of JavaScript frameworks @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 15

Slide 15

Backend Becomes a REST API Serving JSON @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 16

Slide 16

We Gained Functionality But lost simplicity and semantics @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 17

Slide 17

Overkill for Many Applications Sometimes we just need a simple web page with a bit of interactivity @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 18

Slide 18

</> htmx Might Be the Right Solution It’s extended HTML ● Simplicity ● Semantics ● Interactivity @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 19

Slide 19

Too much theory, show us a demo! Examples, examples, examples! example-01.html <script src=”https://unpkg.com/htmx.org@2.0.2”></script> <!— have a button that POST on a click via AJAX and replace the content of #status div with the response —> <button hx-post=”/clicked” hx-target=”#status”> Click Me </button> <div id=”status”>Not yet clicked</div> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 20

Slide 20

Too much theory, show us a demo! example-01.html https://github.com/LostInBrittany/introduction-to-htmx-and-lit @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 21

Slide 21

Sending POST on a button click ./html-examples/html-example-01.html example-01.html <script src=”https://unpkg.com/htmx.org@2.0.2”></script> <!— have a button that POST on a click via AJAX and replace the content of #status div with the response —> <button hx-post=”/clicked” hx-target=”#status”> Click Me </button> <div id=”status”>Not yet clicked</div> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 22

Slide 22

GET, POST, PUT, DELETE… ./html-examples/html-example-02.html <script src=”https://unpkg.com/htmx.org@2.0.2”></script> <div> <button hx-get=”/test-methods” hx-target=”#status”>Send GET</button> <button hx-post=”/test-methods” hx-target=”#status”>Send POST</button> <button hx-put=”/test-methods” hx-target=”#status”>Send PUT</button> <button hx-delete=”/test-methods” hx-target=”#status”>Send DELETE</button> </div> <div id=”status”>No request sent</div> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 23

Slide 23

Using response to replace elements ./html-examples/html-example-03.html <script src=”https://unpkg.com/htmx.org@2.0.2”></script> <div id=”test-replace”> <button hx-get=”/test-replace/innerHTML”> If you click, this message will be replaced </button> <button hx-get=”/test-replace/outerHTML” hx-swap=”outerHTML”> If you click, this button will become a div </button> <button hx-get=”/test-replace/delete” hx-swap=”delete”> If you click, this button will disappear when the response is received </button> <button hx-get=”/test-replace/none” hx-swap=”none”> If you click, nothing changes, the response is ignored </button> </div> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 24

Slide 24

Choosing when to send requests ./html-examples/html-example-04.html <script src=”https://unpkg.com/htmx.org@2.0.2”></script> <!— By default, AJAX requests are triggered by the “natural” event of an element: —> <div id=”test-triggers”> <button hx-get=”/trigger/natural” hx-target=”#status”> In a button the natural event is a click </button> <button hx-trigger=”mouseover” hx-get=”/trigger/mouseover” hx-target=”#status”> This button triggers on mouseover </button> <button hx-trigger=”mouseenter” hx-get=”/trigger/mouseenter” hx-target=”#status”> This button triggers on mouseenter </button> <button hx-trigger=”mouseleave” hx-get=”/trigger/mouseleave” hx-target=”#status”> This button triggers on mouseleave </button> </div> <div id=”status”>No AJAX request sent yet</div> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 25

Slide 25

More triggering options ./html-examples/html-example-05.html <script src=”https://unpkg.com/htmx.org@2.0.2”></script> <!— By default, AJAX requests are triggered by the “natural” event of an element: —> <div id=”test-triggers”> <button hx-trigger=”every 5s” hx-get=”/trigger/5seconds” hx-target=”#status”> Sends request every 5 seconds, no event needed </button> <button hx-trigger=”click[ctrlKey]” hx-get=”/trigger/ctrlclick” hx-target=”#status”> Sends request on click while pressing Ctrl </button> <button hx-trigger=”click[ctrlKey] once” hx-get=”/trigger/ctrlclickonce” hx-target=”#status”> Sends request on the first click while pressing Ctrl </button> </div> <div id=”status”>No AJAX request sent yet</div> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 26

Slide 26

A spinner to ease you wait ./html-examples/html-example-06.html <script src=”https://unpkg.com/htmx.org@2.0.2”></script> <!— By default, AJAX requests are triggered by the “natural” event of an element: —> <div id=”test-triggers”> <button hx-trigger=”every 5s” hx-get=”/trigger/5seconds” hx-target=”#status”> Sends request every 5 seconds, no event needed </button> <button hx-trigger=”click[ctrlKey]” hx-get=”/trigger/ctrlclick” hx-target=”#status”> Sends request on click while pressing Ctrl </button> <button hx-trigger=”click[ctrlKey] once” hx-get=”/trigger/ctrlclickonce” hx-target=”#status”> Sends request on the first click while pressing Ctrl </button> </div> <div id=”status”>No AJAX request sent yet</div> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 27

Slide 27

Des extensions presque à l’infini @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 28

Slide 28

Time for More Code! Let’s see a complete example @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 29

Slide 29

Too much theory, show us a demo! example-01.html https://github.com/LostInBrittany/introduction-to-htmx-and-lit @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 30

Slide 30

Let’s do a to-do list From Hello World to a To-do List @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 31

Slide 31

What the heck are web component? The 3 minutes context @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 32

Slide 32

Web Components Web standard W3C @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 33

Slide 33

Web Components Available in all modern browsers: Firefox, Safari, Chrome @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 34

Slide 34

Web Components Create your own HTML tags Encapsulating look and behavior @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 35

Slide 35

Web Components Fully interoperable With other web components, with any framework @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 36

Slide 36

Web Components CUSTOM ELEMENTS @PierreZ - @LostInBrittany SHADOW DOM TEMPLATES DEVOXX FRANCE 2025

Slide 37

Slide 37

Custom Element To define your own HTML tag <body> … <script> window.customElements.define(‘my-element’, class extends HTMLElement {…}); </script> <my-element></my-element> </body> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 38

Slide 38

Shadow DOM To encapsulate subtree and style in an element <button>Hello, world!</button> <script> var host = document.querySelector(‘button’); const shadowRoot = host.attachShadow({mode:’open’}); shadowRoot.textContent = ‘こんにちは、影の世界!’; </script> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 39

Slide 39

Template To have clonable document template <template id=”mytemplate”> <img src=”” alt=”great image”> <div class=”comment”></div> </template> var t = document.querySelector(‘#mytemplate’); // Populate the src at runtime. t.content.querySelector(‘img’).src = ‘logo.png’; var clone = document.importNode(t.content, true); document.body.appendChild(clone); @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 40

Slide 40

But in fact, it’s just an element… ● ● ● ● Attributes Properties Methods Events @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 41

Slide 41

Simple. Fast. Web Components @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 42

Slide 42

Modern lightweight web components For the new web paradigm @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 43

Slide 43

LitElement import { LitElement, html } from ‘lit-element’; // Create your custom component class CustomGreeting extends LitElement { // Declare properties static get properties() { return { name: { type: String } }; } // Initialize properties constructor() { super(); this.name = ‘World’; } // Define a template render() { return html<p>Hello, ${this.name}!</p>; } } // Register the element with the browser customElements.define(‘custom-greeting’, CustomGreeting); Lightweight web-components using lit-html @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 44

Slide 44

Based on lit-html An efficient, expressive, extensible HTML templating library for JavaScript @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 45

Slide 45

Do you know tagged templates? function uppercaseExpression(strings, …expressionValues) { var finalString = ” for ( let i = 0; i < strings.length; i++ ) { if (i > 0) { finalString += expressionValues[i - 1].toUpperCase() } finalString += strings[i] } return finalString } const expressions = [ ‘Sophia Antipolis’, ‘RivieraDev’, ‘Thank you’]; console.log(uppercaseJe suis à ${expression[0]} pour ${expression[1]. $expression[2]! Little known functionality of template literals @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 46

Slide 46

lit-html Templates let myTemplate = (data) => html<h1>${data.title}</h1> <p>${data.body}</p>; Lazily rendered Generates a TemplateResult @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 47

Slide 47

It’s a bit like JSX, isn’t it? The good sides of JSX… but in the standard! @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 48

Slide 48

Too much theory, show us a demo! example-01.html https://github.com/LostInBrittany/introduction-to-htmx-and-lit @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 49

Slide 49

Custom Greeting example import { LitElement, html } from ‘lit-element’; // Create your custom component class CustomGreeting extends LitElement { // Declare properties static get properties() { return { name: { type: String } }; } // Initialize properties constructor() { super(); this.name = ‘World’; } // Define a template render() { return html<p>Hello, ${this.name}!</p>; } } // Register the element with the browser customElements.define(‘custom-greeting’, CustomGreeting); Lightweight web-components using lit-html @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 50

Slide 50

My Lit Counter example Let’s do an interactive counter @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 51

Slide 51

Lit & </> htmx Love at first <tag> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 52

Slide 52

htmx for structure, Lit to encapsulate logic To htmx, Lit elements are just regular tags @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 53

Slide 53

Too much theory, show us a demo! example-01.html https://github.com/LostInBrittany/introduction-to-htmx-and-lit @PierreZ - @LostInBrittany DEVOXX FRANCE 2025

Slide 54

Slide 54

That’s all, folks! Thank you all! @PierreZ - @LostInBrittany r u o ey v a e l e s a Ple ack! b d e e f DEVOXX FRANCE 2025