<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Paolo Rabbito</title>
    <description>The latest articles on DEV Community by Paolo Rabbito (@paolorabbito).</description>
    <link>https://dev.to/paolorabbito</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2848163%2F75c15fa0-34cc-47b0-8a20-8fa85cc48042.jpeg</url>
      <title>DEV Community: Paolo Rabbito</title>
      <link>https://dev.to/paolorabbito</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paolorabbito"/>
    <language>en</language>
    <item>
      <title>Modern MVC with HTMX</title>
      <dc:creator>Paolo Rabbito</dc:creator>
      <pubDate>Fri, 06 Jun 2025 15:05:35 +0000</pubDate>
      <link>https://dev.to/claranet/modern-mvc-with-htmx-3a7f</link>
      <guid>https://dev.to/claranet/modern-mvc-with-htmx-3a7f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“We need an admin panel that’s only accessible to administrators, not regular users.”&lt;br&gt;
“I’d like a dashboard where I can track certain key metrics.”&lt;br&gt;
“It would be great to have a web page where, as an admin, I can upload articles or manage users.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you’ve ever worked on an e-commerce platform, a CRM, or similar projects, chances are you’ve heard requests like these. Usually, they’re followed by something like: “It doesn’t have to be too complex—after all, regular users won’t see it.”&lt;/p&gt;

&lt;h1&gt;Issues with Traditional Approaches&lt;/h1&gt;

&lt;p&gt;To build our application, we could choose a tool like Angular or React. Alternatively, if we’re working with a backend framework such as Django or Symfony, we might opt for a template engine combined with some simple JavaScript.&lt;/p&gt;

&lt;p&gt;However, both of these approaches have their drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Using tools like Angular or React requires additional knowledge, more time to start up, introduces extra dependencies that need to be maintained and updated, and ultimately adds unnecessary complexity to the project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Relying on HTML with a template engine and vanilla JavaScript is simpler in theory, but no one enjoys dealing with long stretches of JavaScript just to handle basic interactions that most libraries can take care of effortlessly nowadays.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And what if our frontend team is overloaded, and this task needs to be handled by someone who’s not comfortable with those technologies?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s where HTMX might be exactly what we need!&lt;/p&gt;

&lt;h1&gt;What's HTMX&lt;/h1&gt;

&lt;p&gt;From the &lt;a href="https://htmx.org/docs/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; we can read that: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"htmx is a library that allows you to access modern browser features directly from HTML, rather than using javascript."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;HTMX's tag-like attributes simplify interaction with the backend while keeping the logic in the HTML layer. This makes development more intuitive and reduces JavaScript complexity in many scenarios.&lt;/p&gt;

&lt;p&gt;When applied to our use case, HTMX offers several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since we only need to write HTML, our code is cleaner, more understandable, and easier to maintain.&lt;/li&gt;
&lt;li&gt;The absence of JavaScript and related libraries makes it more accessible for backend developers, and reduces ongoing maintenance requirements.&lt;/li&gt;
&lt;li&gt;Development is faster, as HTMX takes care of most of the necessary logic for us.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmm1t0ayaq70ze7pm9mr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmm1t0ayaq70ze7pm9mr.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;The Simplicity of HTMX&lt;/h1&gt;

&lt;p&gt;HTMX provides us with several useful tools for making AJAX calls, replacing parts of HTML with the received responses, triggering requests on certain events, and much more.&lt;/p&gt;

&lt;p&gt;HTMX makes simplicity its greatest strength for several reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is so easy to integrate into your projects.&lt;/li&gt;
&lt;li&gt;It doesn't come with heavy dependencies, doesn’t require TypeScript, nor does it rely on ES6. This makes it perfect for browser-based applications and even compatible with Internet Explorer 11.&lt;/li&gt;
&lt;li&gt;Its lack of dependencies and the straightforward way HTMX’s code is designed make it ideal for the open-source world, encouraging user contributions and collaboration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;How to start an HTMX project&lt;/h4&gt;

&lt;p&gt;HTMX is a dependency-free, browser-oriented javascript library. To use it we can simply add a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag to our document head and we are ready to go.&lt;/p&gt;

&lt;p&gt;We can include HTMX via CDN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Modern MVC with HTMX&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script 
   &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/htmx.org@2.0.4"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or we can download from &lt;a href="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js" rel="noopener noreferrer"&gt;here&lt;/a&gt; and include the file in our HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Modern MVC with HTMX&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/path/to/htmx.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also install it in our application as an external dependency using our favorite package manager. This could be useful in case we want to extend a React application with HTMX.&lt;/p&gt;

&lt;p&gt;Installing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;htmx.org@2.0.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add import in our javascript/typescript file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;htmx.org&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this article we will use it via CDN.&lt;/p&gt;

&lt;h4&gt;AJAX Requests&lt;/h4&gt;

&lt;p&gt;HTMX provides a range of attributes to handle HTTP requests directly in HTML, with each one serving a specific purpose.&lt;/p&gt;

&lt;h6&gt;hx-get&lt;/h6&gt; 

&lt;p&gt;Can be used to send HTTP GET requests. It is typically employed to retrieve data from the server for display or interaction without requiring form submissions. When combined with hx-params, you can specify query parameters to dynamically modify the request. &lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/api/data"&lt;/span&gt; &lt;span class="na"&gt;hx-params=&lt;/span&gt;&lt;span class="s"&gt;"key=value"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Fetch Data&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sends a GET request to /api/data with the parameter key=value.&lt;/p&gt;

&lt;h6&gt;hx-post,hx-put and hx-patch&lt;/h6&gt;

&lt;p&gt;These attributes send HTTP POST/PUT/PATCH requests, commonly used to submit data to the server, such as form inputs or payloads. This is ideal for operations that create or update resources. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/api/save"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above sends the form data to /api/save using a POST request but it can easily be changed with &lt;code&gt;hx-put&lt;/code&gt; or &lt;code&gt;hx-patch&lt;/code&gt;, depending on type of request we want to send.&lt;/p&gt;

&lt;h6&gt;hx-delete&lt;/h6&gt; 

&lt;p&gt;Can be used to send HTTP DELETE requests, intended for removing a resource from the server. Simple and effective, it works as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;hx-delete=&lt;/span&gt;&lt;span class="s"&gt;"/api/delete"&lt;/span&gt; &lt;span class="na"&gt;hx-params=&lt;/span&gt;&lt;span class="s"&gt;"id=456"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Delete Resource&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sends a DELETE request to remove the resource identified by id=456.&lt;/p&gt;

&lt;h4&gt;Triggering Requests&lt;/h4&gt;

&lt;p&gt;It’s possible to trigger requests with different events using the &lt;code&gt;hx-trigger&lt;/code&gt; attribute. By setting specific values for this attribute, you can control when a request is fired. Below are some examples of commonly used values for &lt;code&gt;hx-trigger&lt;/code&gt;:&lt;/p&gt;

&lt;h6&gt;input&lt;/h6&gt; 

&lt;p&gt;Fires the request on every keystroke, perfect for real-time updates (such as live search results).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/search"&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;change&lt;/h6&gt;

&lt;p&gt;Fires the request only when the element loses focus or when its value changes (great for dropdowns or form fields).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/api/filter"&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"change"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"all"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;All&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Active&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"completed"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Completed&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;load&lt;/h6&gt; 

&lt;p&gt;automatically once the element is initially loaded onto the page. This is useful for dynamic content loading or initializing parts of a webpage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/api/data"&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"load"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;delay&lt;/h6&gt; 

&lt;p&gt;This value lets you delay the request by a specified amount of time after the triggering event. If another event of the same type occurs during the delay period, the countdown resets, ensuring only one request is sent after the delay. Could be useful in live search combined with other type of trigger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/search"&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"input delay:500ms"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check all the possible use case of triggers you can check the &lt;a href="https://htmx.org/docs/#triggers" rel="noopener noreferrer"&gt;docs&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Handle Response&lt;/h4&gt;

&lt;p&gt;HTMX provides attributes such as hx-target and hx-swap to control how and where a server’s response is applied to the DOM. These attributes give you precise control over your page's dynamic behavior without requiring JavaScript.&lt;/p&gt;

&lt;h6&gt;hx-swap&lt;/h6&gt; 

&lt;p&gt;This attribute lets you define how the response content from the server is swapped into the target element (or the element that made the request). The default behavior is to replace the inner content of the targeted element using &lt;code&gt;innerHTML&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/api/message"&lt;/span&gt; &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Get Message&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;outerHTML&lt;/code&gt;replaces the entire target element (including the tag itself) with the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/api/card"&lt;/span&gt; &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Fetch Card&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check every possible values read the &lt;a href="https://htmx.org/attributes/hx-swap/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;&lt;/p&gt;

&lt;h6&gt;hx-target&lt;/h6&gt; 

&lt;p&gt;This attribute allows you to specify which element in the DOM will be swapped with the server's response, rather than swapping the element that triggered the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/api/message"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#output"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Get Message&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"output"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server's response replaces the content of the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with &lt;code&gt;id="output"&lt;/code&gt; (using the default &lt;code&gt;innerHTML&lt;/code&gt; swap behavior).&lt;/p&gt;

&lt;h1&gt;Practical Use Cases of HTMX&lt;/h1&gt;

&lt;p&gt;Many dashboards share common features, such as pagination, real-time data polling to update charts, form validation for adding new users or other entities, bulk file uploads, and table search functionality. HTMX simplifies the implementation of all these tasks—and much more—making development faster and more streamlined.&lt;/p&gt;

&lt;p&gt;In this article, I’ll guide you through several practical examples to showcase HTMX's capabilities. However, I strongly recommend exploring the &lt;a href="https://htmx.org/docs/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; for a comprehensive overview of its features and use cases.&lt;/p&gt;

&lt;p&gt;The examples presented here are built using HTMX, Tailwind CSS, and Twig (the template engine used by the Symfony framework). That said, these examples can be easily adapted to work with any backend language or template engine.&lt;/p&gt;

&lt;h2&gt;Form Validation&lt;/h2&gt;

&lt;p&gt;HTMX can be used to implement real-time form validation by sending data to the server to check its validity and dynamically displaying error messages or confirmations. For this, you can use attributes like &lt;code&gt;hx-post&lt;/code&gt;, &lt;code&gt;hx-trigger&lt;/code&gt;, &lt;code&gt;hx-target&lt;/code&gt;, and &lt;code&gt;hx-swap&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0t5couj7bpkmyk5xanwd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0t5couj7bpkmyk5xanwd.gif" alt=" "&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhiixobahkc1tz2xcjw4u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhiixobahkc1tz2xcjw4u.gif" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Controller:&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="na"&gt;#[Route('/validate-email', name: 'validate_email', methods: ['POST'])]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validateEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Mock validation logic (replace with your validation logic)&lt;/span&gt;
    &lt;span class="nv"&gt;$isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;filter_var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;FILTER_VALIDATE_EMAIL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Return a user-friendly message formatted for display&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;span class="text-sm text-green-600"&amp;gt;Email is valid!&amp;lt;/span&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;span class="text-sm text-red-600"&amp;gt;Please enter a valid email address.&amp;lt;/span&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;FormHTML&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/validate-email"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#email-validation-result"&lt;/span&gt; &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block text-sm font-medium text-gray-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border border-gray-300 px-3 py-2 rounded-md"&lt;/span&gt;
    &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"change delay:500ms"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email-validation-result"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-sm mt-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-blue-500 text-white px-3 py-2 rounded hover:bg-blue-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;Active Searching&lt;/h2&gt;

&lt;p&gt;To implement active searching we need to use &lt;code&gt;hx-get&lt;/code&gt;to send get request, &lt;code&gt;hx-params&lt;/code&gt; to add query params to our request, &lt;code&gt;hx-target&lt;/code&gt; to point to our table that need to be populated with filtered result and &lt;code&gt;hx-trigger&lt;/code&gt; to define when and how the get request should be sent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75mzq5wip7tdpzv2vxv4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75mzq5wip7tdpzv2vxv4.gif" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Controller:&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="na"&gt;#[Route('/users_search', name: 'users_search', methods: ['GET'])]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;MapQueryParameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//return html file to frontend if is the first load&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HX-Request'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users/search_user.html'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;//how to filter result depends on your system&lt;/span&gt;
    &lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;//return table rows with filtered data&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;buildResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$users&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;Building response:&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;buildResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$users&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$html&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="s1"&gt;'&amp;lt;tr&amp;gt;
         &amp;lt;td class="px-6 py-4 whitespace-nowrap"&amp;gt;
         &amp;lt;div class="text-sm text-blue-900"&amp;gt;'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;/div&amp;gt;
         &amp;lt;/td&amp;gt;
         &amp;lt;td class="px-6 py-4 whitespace-nowrap"&amp;gt;
         &amp;lt;div class="text-sm text-blue-900"&amp;gt;'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;/div&amp;gt;
         &amp;lt;/td&amp;gt;
         &amp;lt;td class="px-6 py-4 whitespace-nowrap"&amp;gt;
         &amp;lt;div class="text-sm text-blue-900"&amp;gt;'&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'last_name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;/div&amp;gt;
         &amp;lt;/td&amp;gt;
         &amp;lt;/tr&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;search_user.html:&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--....other html....--&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control border border-gray-300 rounded-md px-3 py-2 mb-4"&lt;/span&gt; 
&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; 
&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Begin Typing To Search Users..."&lt;/span&gt;
&lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/users_search"&lt;/span&gt;
&lt;span class="na"&gt;hx-params=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt;
&lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"input changed delay:500ms, keyup[key=='Enter'], load"&lt;/span&gt;
&lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#search-results"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!--....other html....--&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;tbody&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"search-results"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;Polling&lt;/h2&gt;

&lt;p&gt;To implement a polling system we need to use &lt;code&gt;hx-get&lt;/code&gt;to send multiple get request, &lt;code&gt;hx-swap&lt;/code&gt; to update the html with the response and &lt;code&gt;hx-trigger&lt;/code&gt; to get data when we load the page and every "x" seconds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjltbgheicys21uvj8gvo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjltbgheicys21uvj8gvo.gif" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Controller&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="na"&gt;#[Route('/polling/data', name: 'polling_data', methods: ['GET'])]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;pollingData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$companies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;companyService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCompanyWithUpdate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;//return update about growth for each company&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;buildResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$companies&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;Build response:&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;buildResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$companies&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$companies&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$company&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$html&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="s1"&gt;'&amp;lt;tr &amp;gt; 
         &amp;lt;td class="px-6 py-4 whitespace-nowrap"&amp;gt;  
         &amp;lt;div class="text-sm text-blue-900"&amp;gt;'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$company&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;/div&amp;gt;
         &amp;lt;/td&amp;gt;
         &amp;lt;td class="px-6 py-4 whitespace-nowrap"&amp;gt;  
         &amp;lt;div class="text-sm text-blue-900"&amp;gt;'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$company&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'growth'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;/div&amp;gt;
         &amp;lt;/td&amp;gt;
         &amp;lt;/tr&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;polling_table.html:&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--...other html....--&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tbody&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white divide-y divide-blue-100"&lt;/span&gt; 
       &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/polling/data"&lt;/span&gt; 
       &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"load, every 3s"&lt;/span&gt; 
       &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!--...other html....--&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;Pagination&lt;/h2&gt;

&lt;p&gt;To implement pagination i'll show you a different approach using a template engine (Twig in this case).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4l1xio0fu6to4nsnfrn6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4l1xio0fu6to4nsnfrn6.gif" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In these example we have used &lt;code&gt;hx-delete&lt;/code&gt; to send the delete request with the &lt;code&gt;hx-confirm&lt;/code&gt;,&lt;code&gt;hx-swap&lt;/code&gt;and &lt;code&gt;hx-target&lt;/code&gt; inside the tbody element.&lt;/p&gt;

&lt;p&gt;Other than delete we have used &lt;code&gt;hx-get&lt;/code&gt; combined with &lt;code&gt;hx-target&lt;/code&gt; and &lt;code&gt;hx-swap&lt;/code&gt;to implement next and prev button.&lt;/p&gt;

&lt;h4&gt;Controller:&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="na"&gt;#[Route('/articles/page', name: 'app_articles_page', methods: ['GET'])]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$articlesPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;articlesService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'page'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pageSize'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'articles/articles.html.twig'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'articles'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$articlesPage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'currentPage'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$articlesPage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'totalPages'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$articlesPage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;totalPages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'nextPage'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$articlesPage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nextPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'previousPage'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$articlesPage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;previousPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;Article's table:&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;tbody&lt;/span&gt; &lt;span class="na"&gt;hx-confirm=&lt;/span&gt;&lt;span class="s"&gt;"Are you sure?"&lt;/span&gt; 
       &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest tr"&lt;/span&gt; 
       &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML swap:1s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
{% for article in articles %}
&lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-6 py-4 whitespace-nowrap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-sm text-blue-900"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ article.id }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-6 py-4 whitespace-nowrap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-sm text-blue-900"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ article.title }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-6 py-4 whitespace-nowrap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-sm text-blue-900"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ article.price }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-6 py-4 whitespace-nowrap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-red-500 hover:bg-red-700 text-white py-2 px-4     rounded"&lt;/span&gt; 
    &lt;span class="na"&gt;hx-delete=&lt;/span&gt;&lt;span class="s"&gt;"/articles/{{ article.id }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Cancel
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
{% endfor %}
&lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;Article's button:&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-center mt-6 space-x-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-3 py-1 bg-blue-700 text-white rounded hover:bg-blue-800 disabled:opacity-50"&lt;/span&gt;
        &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/articles/page?page={{ previousPage }}"&lt;/span&gt;
        &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#articles-table-and-pagination"&lt;/span&gt;
        &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;
&lt;span class="err"&gt;{%&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="na"&gt;previousPage&lt;/span&gt; &lt;span class="na"&gt;is&lt;/span&gt; &lt;span class="na"&gt;null&lt;/span&gt; &lt;span class="err"&gt;%}&lt;/span&gt;&lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="err"&gt;{%&lt;/span&gt; &lt;span class="na"&gt;endif&lt;/span&gt; &lt;span class="err"&gt;%}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Prev&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-3 py-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
Pagina {{ currentPage }} di {{ totalPages }}
&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-3 py-1 bg-blue-700 text-white rounded hover:bg-blue-800 disabled:opacity-50"&lt;/span&gt;
        &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/articles/page?page={{ nextPage }}"&lt;/span&gt;
        &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#articles-table-and-pagination"&lt;/span&gt;
        &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;
&lt;span class="err"&gt;{%&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="na"&gt;nextPage&lt;/span&gt; &lt;span class="na"&gt;is&lt;/span&gt; &lt;span class="na"&gt;null&lt;/span&gt; &lt;span class="err"&gt;%}&lt;/span&gt;&lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="err"&gt;{%&lt;/span&gt; &lt;span class="na"&gt;endif&lt;/span&gt; &lt;span class="err"&gt;%}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Next&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Implementing features like active searching, and many others, becomes remarkably simple with HTMX, requiring you to write almost exclusively HTML. This simplicity reduces development complexity while maintaining powerful functionality for dynamic, interactive web pages.&lt;/p&gt;

&lt;p&gt;HTMX is an excellent choice for projects where advanced graphical functionality isn’t a priority, such as administration panels, dashboards, or other content-focused applications. By eliminating the need for extensive JavaScript frameworks, HTMX helps cut development costs and speeds up the delivery process, all while keeping your codebase easier to maintain.&lt;/p&gt;

&lt;p&gt;Moreover, HTMX bridges the gap for backend-oriented developers, empowering them to create dynamic frontend features without requiring deep knowledge of modern JavaScript tooling. Its lightweight, dependency-free nature makes it not only cost-effective but also accessible to a wider range of programmers.&lt;/p&gt;

&lt;p&gt;Whether you're building practical dashboards, simplifying server-client interactions, or just striving for cleaner code, HTMX provides the flexibility and efficiency to meet your goals. Combined with its compatibility across various template engines and backend languages, HTMX becomes a versatile and indispensable tool for modern web development.&lt;/p&gt;

&lt;p&gt;If you'd like to dive deeper into practical examples, you can find the repository &lt;a href="https://github.com/claranet-it/devrel-mvc-with-HTMX" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>htmx</category>
      <category>development</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
