Exploring HTMX: building dynamic web apps without JavaScript
According to the documentation, the HTMX library allows you to add modern browser features without using Javascript. It gives you access to CSS Transitions, AJAX, WebSockets, and Server-Sent Events directly in HTML, using attributes to build modern user interfaces quickly. Would it be possible to develop web apps on the client-side without JavaScript? In this tutorial, we will explore all the mind-blowing features of HTMX. You will learn how to send Ajax requests, upload files, validate input, and trigger CSS transitions without writing JavaScript code.
What is HTMX, and why is it important?
In 2013, Carson Gross created an alternative frontend library intercooler.js, with the tagline “Ajax With Attributes” to simplify the complexity of frontend web development. A new version of intercooler.js reached version 2.0 and became htmx described as a library that enables you to access Ajax, WebSockets, CSS transition, and Server-Sent Events directly inside HTML.
The HTMX creator says it attempts to leverage HTML’s power by using the original model of the web to create web apps. With this approach, developers don’t have to write JavaScript to achieve similar functionality. Instead, they use extra HTML attributes to achieve dynamic content and updates. According to Gross, these are the motivation behind htmx:
- Why should only
<a>
and<form>
be able to make HTTP requests? - Why should only
click
&submit
events trigger them? - Why should only
GET
&POST
methods be available? - Why should you only be able to replace the
entire
screen?
“By removing the arbitrary constraints, htmx completes HTML as a hypertext,” he concludes.
With HTMX, developers can implement several UI features and UX patterns with minimal HTML and styling such as Progress Bar, Lazy Loading, Infinite Scroll, Inline Validation and more.
The HTMX approach differs from other frontend frameworks such as Vue.js and React, where the client-side application uses JavaScript to request information from the server and receive it in a JSON format. With HTMX, when you make a request to the server, the endpoint will return Html fully formed and will update part of the page. You can integrate HTMX with any server-side technology since application logic happens on the backend.
Setting up HTMX
To setup HTMX, you can seemingly load it via a CDN and add it to your head tag like this below:
<script src="https://unpkg.com/htmx.org@1.7.0" integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous"></script
Another way is to download the htmx.min.js source file and add it to the appropriate directory in your project, then insert it where necessary with a script tag like this :
<script src="/path/to/htmx.min.js"></script>
You can also install HTMX via npm
like this below:
npm install htmx.org
Sending Ajax Requests using HTMX
HTMX offers a set of attributes that enable you to make AJAX requests directly from HTML.
hx-post
- issues a POST request to the given URL.hx-get
- issues a GET request to the given URL.hx-put
- issues a PUT request to the given URL.hx-patch
- issues a PATCH request to the given URL.hx-delete
- issues a DELETE request to the given URL.
Each of these attributes above accepts a URL to send an AJAX request to. So whenever the element is triggered, it sends the specified type of request to the given URL. Consider the example below:
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
<div
hx-get="https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode"
>Load Jokes</div>
The demo above instructs the browser that when a user clicks the Load Jokes
element, it should send a GET
request (hx-get
) to the jokeapi endpoint URL (https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode) and load the response into the div element. We used a demo Joke API here, a REST API that serves uniformly and well-formatted jokes.
Next, we will examine how to load the response into another HTML element.
Triggering Requests with HTMX
An element’s “natural” event initiates Ajax requests automatically by default. For example, onchange
event triggers textarea
, input
, and select
. While the onsubmit
event triggers form
. The click
event triggers every other request. HTMX offers a unique ‘hx-trigger’ if you need to specify which event will trigger the request.
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
<div class= "jokes"
hx-get="https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode"
hx-trigger="mouseenter”
>
Load Jokes
</div>
When the mouse hovers over the div element in the code above, it sends a GET request to the provided URL endpoint and retrieves the Jokes.
Triggering Modifiers with HTMX
As mentioned in the previous section, it is possible to modify the hx-trigger attribute to change its behavior. For instance, if you only want a request to occur once, you can use the once
modifier for the trigger.
<div class= "jokes"
hx-get="https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode"
hx-trigger="mouseenter once”
>
Load Jokes
</div>
Below are the lists of available modifiers:
-
changed
- send a request if the element value has changed. -
display:<time interval>
- wait for a given amount of time(e.gdelay-1s
) before sending the request. -
throttle: <time interval>
- wait the given amount of time (e.gdelay-1s
) before sending the request. Unlike delay, if a new event occurs before it reaches the time limit, the event will discard, which means it triggers a request at the end of the time. -
form
:<css selector>
- listen for event on a different element. You can use this for things like keyboard shortcuts.
You can use all these attributes above for implementing some common UX patterns like the Active Search Box pattern. With this example here, you can see these attributes above in action.
Polling with the htmx-trigger
attribute
Using the HTMX trigger attribute, we can also specify that the element polls the given URL every n
seconds instead of waiting for an event to occur.
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
<div class= "jokes" hx-get="https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode" hx-trigger="every 3s">
Load Jokes
</div>
Here, the HTMX instructs the browser to send a GET request to the /V2.jokeapi
URL every 3s and display the response in the div element.
Request Indicators
Because the browser provides no feedback, it is helpful to notify users that something is happening. You can do this with HTMX by using the ‘htmx-indicator’ class.
<div class= "jokes" hx-get="https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode" >
<button>Load Jokes</button>
<img class="htmx-indicator" src="https://i.imgur.com/8THYvPQ.gif">
</div>
The ‘htmx-indicator’ class would set the opacity of any element with this class to 0
by default, making it hidden but present in the DOM. When you click the ‘load jokes’ button, it adds the ‘htmx-request’ class, which displays the loader indicator.
Targeting Elements
As stated before, HTMX loads the response to an AJAX request into the element that initiated the request. The ‘hx-target’ attribute allows you to load the response in a different element than the one that initiated the request. The ‘hx-target’ will accept a CSS selector and will automatically load the AJAX response into the target element:
<button class="btn" hx-get="https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode" hx-target="#result" > Load Jokes
</button>
<div class= "jokes" id="result"></div>
In the demo above, when you click on the load jokes
button, it will automatically load the response into the div
element below it.
Swapping
Like the hx-target
in the previous section, HTMX offers a different way to determine how to load the response returned by Ajax within the Dom. You can do so by setting the ‘hx-swap’ attribute with any of the values listed below:
innerHTML
: This is the default value; it inserts the content into the target element sending the request.outerHTML
: It replaces the entire target element with the returned content.afterbegin
: It prepends the response before the first child inside the target element.beforebegin
: prepends the response as a parent element of the actual element triggering the request.beforeend
: It appends the response after the last child of the element sending the request.afterend
: like thebeforeend
, this appends response after the element sending request.none
: this option doesn’t append or prepend a response from an AJAX request.
Here is an example of a scrolling progress bar using some of the attributes mentioned above (https://htmx.org/examples/progress-bar).
Synchronizing Request with HTMX
Sometimes, you will need to synchronize requests between two elements. Suppose you want a request from one element to override another element’s requests, or you want to wait until the other element’s requests are complete. You can do this by using the hx-sync
attribute. Consider the code below:
<form hx-post="/article">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
>
<button type="submit">Submit</button>
</form>
In the example above, we have a form submission and an individual input
validation request. Without using hx-sync
, when you fill out the form and submit it, it triggers two parallel requests to /change
and /validate
simultaneously. According to the documentation, using hx-sync=" closest form:abort"
on the input will watch for requests on the form
and halt the input requests if a form request is present or start while the input request is in flight:
<form hx-post="/article">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:abort"
>
<button type="submit">Submit</button>
</form>
Using this approach, we can fix the synchronization issue between the two elements declaratively. You can read more on hx-sync
attribute here.
Open Source Session Replay
OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.
Start enjoying your debugging experience - start using OpenReplay for free.
File Upload with HTMX
With HTMX, you can create a file upload form that will be submitted via Ajax to your backend for processing. You can effortlessly send files like videos, images, and documents. You can implement this with HTMX by directly embedding the hx-encoding
attributes with the value multipart/form-data
into the parent element sending the request:
<form hx-encoding='multipart/form-data' hx-post='/registration'
_='on htmx:xhr:progress(loaded, total) set #progress.value to (loaded/total)*100'>
<input type='file' name='userFile'>
<button>
Upload File
</button>
</form>
Input Validation with HTMX
Htmx incorporates natively with the HTML 5 Validation API, so if a validatable input is invalid, it will not send a request. This feature applies to both AJAX requests and WebSockets sends. Also, HTMX fires events around validation that you can use to hook in custom validation and error handling. The following events are currently available:
- `htmx:validation: validate – used for adding custom validation logic.
htmx
:validation:failed` – this event fires when an element validation return false, e.g., indicating an invalid input.htmx:validation: halted
: It calls this event when you a request is not issued due to validation errors. You can find specific errors in theevent.detail.errors
object.
Consider an input that uses the htmx:validate:validate
event to ensure that the input has the value David
using hyperscript:
<form hx-post="/validate">
<input _="on htmx:validation:validate
if my.value != 'David'
call me.setCustomValidity('Please enter the value David')
else
call me.setCustomValidity('')"
name="username"
>
</form>
It is important to note that all client-side validation must occur on the server, as it can always be bypassed.
CSS animations with HTMX
Htmx enables you to use CSS transitions to add smooth animations and transitions to your web page using only CSS and HTML without JavaScript. HTMX offers a powerful animation extension called class-tools
which allows you to define CSS classes that swap onto or off the element using the classes
or data-classes
attribute.
You can use the extension by assigning to the classes
attribute to an element. The classes
attribute value consist of “runs” separated by an & character. In a run, it will apply class operations sequentially with the specified delay. These class operations are add
, remove
, or toggle
accompanied by a CSS class name and optionally by a colon: and time delay.
<div classes= “add sample-demo: 1s”></div>
Once the browser contents load, HTMX will automatically add a new class of sample-demo
to the div after 1 second. Let’s take a look at this demo example below:
<div hx-ext="class-tools">
<div class="demo" classes="toggle faded:1s">See me Fading Away </div>
</div>
CodePen-Link You can learn more about how to do compelling animation and transition with HTMX here.
HTMX seamlessly integrates with different server-side frameworks, although some frameworks may have alternatives for installing HTMX. Check out this link to explore how you can integrate HTMX with different server-side frameworks.
Conclusion
HTMX is an incredible technology, and I’m excited about it and can’t wait to use it in production on my next project. In this tutorial, we explored how to install HTMX, send Ajax requests, upload files, validate input, and create CSS transitions without using javaScript on the client-side.
Check this link to explore some sets of UX patterns demo implemented with HTMX that you can edit and integrate with your projects.
The following link (https://htmx.org/examples/) contains some sets of UX pattern demos that you can edit and integrate into your projects.