A presentation at DevoxxFR in in Paris, France by Horacio Gonzalez
</> htmx 2.0 & Web Components A Perfect Match for Frontend Development @PierreZ – @LostInBrittany DEVOXX FRANCE 2025 @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Horacio Gonzalez @LostInBrittany Espagnol Perdu en Bretagne @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Pierre Zemb @PierreZ Ingénieur Perdu dans le front @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Create modern user interfaces with the simplicity and power of hypertext @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
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
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
But, what’s the point? It sounds nice and semantic, but what’s the real benefit? @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
A Quick Look Back A time when dinosaurs like me coded with Struts @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Remember MVC? With its page-by-page navigation @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
The Golden Age of MVC Frameworks Generating HTML views @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
2005: The Arrival of AJAX The birth of Web 2.0 @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Web Pages Become Dynamic Apps Powered by JavaScript and jQuery @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Shift to Single Page Applications (SPA) @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Increasing Complexity The rise of JavaScript frameworks @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Backend Becomes a REST API Serving JSON @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
We Gained Functionality But lost simplicity and semantics @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Overkill for Many Applications Sometimes we just need a simple web page with a bit of interactivity @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
</> htmx Might Be the Right Solution It’s extended HTML ● Simplicity ● Semantics ● Interactivity @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
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
Too much theory, show us a demo! example-01.html https://github.com/LostInBrittany/introduction-to-htmx-and-lit @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
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
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
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
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
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
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
Des extensions presque à l’infini @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Time for More Code! Let’s see a complete example @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Too much theory, show us a demo! example-01.html https://github.com/LostInBrittany/introduction-to-htmx-and-lit @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Let’s do a to-do list From Hello World to a To-do List @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
What the heck are web component? The 3 minutes context @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Web Components Web standard W3C @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Web Components Available in all modern browsers: Firefox, Safari, Chrome @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Web Components Create your own HTML tags Encapsulating look and behavior @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Web Components Fully interoperable With other web components, with any framework @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Web Components CUSTOM ELEMENTS @PierreZ - @LostInBrittany SHADOW DOM TEMPLATES DEVOXX FRANCE 2025
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
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
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
But in fact, it’s just an element… ● ● ● ● Attributes Properties Methods Events @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Simple. Fast. Web Components @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Modern lightweight web components For the new web paradigm @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
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
Based on lit-html An efficient, expressive, extensible HTML templating library for JavaScript @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
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
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
It’s a bit like JSX, isn’t it? The good sides of JSX… but in the standard! @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Too much theory, show us a demo! example-01.html https://github.com/LostInBrittany/introduction-to-htmx-and-lit @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
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
My Lit Counter example Let’s do an interactive counter @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Lit & </> htmx Love at first <tag> @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
htmx for structure, Lit to encapsulate logic To htmx, Lit elements are just regular tags @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
Too much theory, show us a demo! example-01.html https://github.com/LostInBrittany/introduction-to-htmx-and-lit @PierreZ - @LostInBrittany DEVOXX FRANCE 2025
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
Envie de découvrir htmx 2.0 et les Web Components en mettant les mains dans le code ? Dans ce lab, vous apprendrez à utiliser htmx 2.0 pour simplifier le développement frontend, en explorant ses nouvelles fonctionnalités à travers des exercices pratiques et guidés.
Nous passerons ensuite aux Web Components, en construisant ensemble un composant encapsulé et réutilisable à l’aide de Lit, une bibliothèque qui simplifie leur création. Vous apprendrez pas à pas comment intégrer des composants dans un projet existant et les enrichir avec des fonctionnalités dynamiques.
Enfin, nous verrons comment combiner htmx et Lit pour tirer le meilleur parti des deux outils. Vous développerez des exemples concrets qui illustrent comment cette association peut simplifier la création d’applications web modulaires et performantes. À la fin du lab, vous repartirez avec des bases solides et du code prêt à être réutilisé dans vos projets.
The following resources were mentioned during the presentation or are useful additional information.