When building web applications, JavaScript libraries such as React.js comes to mind. This UI library has revolutionized web application development and improved interactivity between applications and users.
While React has made significant strides in enhancing web interactivity, it also comes with a certain level of complexity. This is where HTMX, steps in. It aims to address this complexity by providing a simpler approach to achieving interactivity on the web.
In this article, we'll examine HTMX, compare it to React to see how it ranks and build a Task Manager using HTMX!
HTMX stands for Hypertext markup extension. It was created by Carson Gross to enhance server-rendered HTML pages. HTMX is a lightweight Javascript that lets you build dynamic web applications using plain HTML with minimal Javascript code.
It extends HTML's capabilities by introducing custom attributes. What do these attributes do? They send behind-the-scenes requests in the form of AJAX from the frontend to the backend and render the returned response content on the screen.
The idea with HTMX is that, unlike React, Vue, or Angular, you're not exchanging data between the frontend and backend. Still, instead, your backend prepares the HTML that should be rendered on the screen, which could be an entire page or a fragment.
The driving force behind HTMX is the extension of HTML with custom attributes that enable AJAX requests and content manipulations. Some of the custom attributes include:
hx-get
: performs a GET to the supplied URL.hx-post
: sends a POST to the supplied URL.hx-select
: choose content to substitute from a response.hx-swap
: determines how the material is swapped in outerHTML, beforeend, afterend, etc.hx-target
: specifies the target element to be swapped.hx-trigger
: provides the event that prompts the request.hx-delete
: performs a DELETE on the supplied URL.hx-put
: Sends a PUT to the supplied URL.Let’s take a look at an example below:
1<button hx-post="/like" hx-target="likes">Like</button>
2<span id="likes">0</span>
In the code above, we are telling the browser that when a user clicks the like button
, send an AJAX POST request to the /like
endpoint and then place the response inside the <span>
element with the id
called like
, as this will update the number of likes dynamically.
Now that we have a clear understanding of how HTMX works, let’s go ahead and compare it to React.
In terms of a learning curve, HTMX stands out as it is more approachable for developers familiar with HTML.
React, in contrast, presents a more challenging learning curve due to its heavy reliance on JSX and other JavaScript concepts.
Regarding simplicity, efficient interactions, and backend-driven applications, Htmx is mainly suited for building as it only requires dynamic content loading, form submissions without page reloads, and fundamental client-side interactions.
React, on the other hand, is a go-to choice for building complex, single-page applications (SPAs) that offer a rich user experience.
HTMX leans towards a more declarative deployment style, which utilizes HTML attributes to specify the behavior of elements without having to write a significant amount of JavaScript code.
In contrast, React employs a more immparative programming style.
HTMX forms work by enhancing the regular HTML forms with custom features to manage dynamic behaviors like AJAX submissions.
Below is an example that submits HTML forms using AJAX and updates a part of the page with the response:
1<form hx-post="/submit" hx-target="#result">
2 <label for="name">Name:</label>
3 <input type="text" id="name" name="name" required />
4 <button type="submit">Submit</button>
5</form>
6
7<div id="result"></div>
8
9<script src="https://unpkg.com/htmx.org@1.8.0"></script>
In the code above, the hx-post="/submit"
specifies that the form should be submitted using a POST request to /submit
. The hx-target="#result"
then specifies the element that will be updated with the server response.
React forms require additional setup since they are often controlled within a component, which uses state management to handle input values and form submissions. This gives you more control over the form's behavior and enables for complicated validation and dynamic updates.
Below is an example of a basic React.js form component that handles state and form submission:
1import React, { useState } from 'react';
2
3function MyForm() {
4 const [name, setName] = useState('');
5
6 const handleSubmit = (event) => {
7 event.preventDefault();
8 // Handle form submission logic here
9 console.log('Form submitted with name:', name);
10 };
11
12 return (
13 <form onSubmit={handleSubmit}>
14 <label htmlFor="name">Name:</label>
15 <input
16 type="text"
17 id="name"
18 value={name}
19 onChange={(e) => setName(e.target.value)}
20 required
21 />
22 <button type="submit">Submit</button>
23 </form>
24 );
25}
26
27export default MyForm;
In the code above, the input field is controlled by the component's state, ensuring any changes are reflected in the state.
HTMX components are created by combining normal HTML elements with interactive properties. HTMX components enhance certain elements or areas of a page, making them dynamic and responsive without the need for a comprehensive component architecture.
React components are self-contained and reusable user interface elements. They can be used together to develop complex UI hierarchies.
When deciding between HTMX and React, several essential considerations must be considered, including the project's objectives as well as the capabilities of the development team.
These factors are critical in identifying the most appropriate framework for your project. Below are some of them:
If you're looking to emphasize simplicity, efficiency, and rapid development, HTMX is the preferred choice for your project. It is useful especially in backend driven-application scenarios where the frontend largely displays data retrieved from the server.
If you're looking for an application that requires advanced interaction, scalability, and a rich user experience, React should be your go-to choice.
While HTMX offers you a simple and straightforward approach to building web applications, there are other HTMX alternatives that can also help do the job. Some of these are:
HTMX is a simple as simple as loading it via CDN into your webpage:
1<script src="https://unpkg.com/htmx.org@1.9.12" integrity="sha384-ujb1lZYygJmzgSwoxRggbCHcjc0rB2XoQrxeTUQyRjrOnlCoYta87iKBWq3EsdM2" crossorigin="anonymous"></script>
While this is regarded as the fastest way to use HTMX, it is not advisable using it for production
You could also self-serve through a minified library or install it via npm:
npm install htmx.org
NOTE: If you encounter permission access error, please run the installation as an admin or use the
sudo
along with the installation command above
To showcase HTMX's simplicity and directness, we'll build a task manager application that allows users to add tasks, view them, and mark them as completed by deleting them. This will help demonstrate HTMX’s ability to enhance HTML with minimal Javascript, in contrast with React’s component-based architecture and Javascript usage.
To set up the environment, you must first ensure we have Node.js installed. If you have that, then you can go ahead and create a new project and install the necessary packages using the following commands:
mkdir htmx-task-manager
cd htmx-task-manager
npm init -y
npm install express ejs body-parser
In the installation command above, we installed the following dependencies.
[express](https://www.npmjs.com/package/express)
: Express.js is a Next.js framework used for building RESTful APIs. [ejs](https://npmjs.com/package/ejs)
: This is a templating engine used for generating HTML on the server side.[body-parser](https://www.npmjs.com/package/body-parser)
: This is a Node.js library for extracting information from an HTTP request.Here's how our folder structure will look like. Proceed to create the various folders and files.
htmx-task-manager/
│
├── public/
│ └── style.css
├── views/
│ ├── index.html
│ └── partials/
│ └── task_list.html
├── app.js
└── package.json
The first thing is to set up the HTML basic structure the task manager. This will consist of a form for adding task and also a list to display them. Inside the views/index.html
.
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <title>Task Manager</title>
6 <link rel="stylesheet" href="/style.css" />
7 <script src="https://unpkg.com/htmx.org@1.6.1"></script>
8 </head>
9 <body>
10 <h1>Task Manager</h1>
11 <form
12 hx-post="/add"
13 hx-target="#task-list-container"
14 hx-swap="innerHTML"
15 hx-on="htmx:afterRequest"
16 >
17 <input
18 type="text"
19 name="task"
20 placeholder="Enter a new task..."
21 required
22 />
23 <button type="submit">Add Task</button>
24 </form>
25
26 <div id="task-list-container">
27 <% tasks.forEach(function(task) { %>
28 <div id="task-<%= task.id %>">
29 <%= task.task %>
30 <button
31 hx-post="/delete/<%= task.id %>"
32 hx-target="#task-list-container"
33 hx-swap="innerHTML"
34 >
35 Delete
36 </button>
37 </div>
38 <% }); %>
39 </div>
40
41 <script>
42 document.body.addEventListener("htmx:afterRequest", function (evt) {
43 if (evt.detail.elt.matches("form")) {
44 evt.detail.elt.reset();
45 }
46 });
47 </script>
48 </body>
49</html>
NOTE: We loaded
HTMX
as a script inside thehead
element of the code.
In the code above, we created a form that will allow users to add new tasks to a task list. When a user submits the form, HTMX sends the POST request to the \add
endpoint on the server with the task details.
Once a response is received from the server, the content of the #task-list-container
div will be replaced with a newly returned task list. Which now contains the newly added task.
Next, inside the views/partials/task_list.html
, add the folowing code:
1<% tasks.forEach(function(task) { %>
2<div id="task-<%= task.id %>">
3 <%= task.task %>
4 <button
5 hx-post="/delete/<%= task.id %>"
6 hx-target="#task-list-container"
7 hx-swap="innerHTML"
8 >
9 Delete
10 </button>
11</div>
12<% }); %>
The code above generates a list of tasks, each corresponding with a “delete button”. When you click the button, you send a request to the server to remove the task, and the list is automatically updated to reflect the changes. This is due to HTMX’s ability to handle asynchronous requests and update the DOM accordingly.
Lastly, for our HTML structure, you need to add some styling. Inside the public/style.css
file, add the following styles:
1body {
2 font-family: Arial, sans-serif;
3 background-color: #f0f0f0;
4 margin: 0;
5 padding: 20px;
6}
7
8h1 {
9 color: #333;
10}
11
12form {
13 margin-bottom: 20px;
14}
15
16input[type="text"] {
17 padding: 10px;
18 margin-right: 10px;
19 border: 1px solid #ccc;
20 border-radius: 4px;
21 width: 300px;
22}
23
24button {
25 padding: 10px 20px;
26 border: none;
27 border-radius: 4px;
28 background-color: #007bff;
29 color: white;
30 cursor: pointer;
31}
32
33button:hover {
34 background-color: #0056b3;
35}
36
37#task-list-container {
38 list-style-type: none;
39 padding: 0;
40}
41
42#task-list-container div {
43 display: flex;
44 justify-content: space-between;
45 align-items: center;
46 background-color: white;
47 padding: 10px;
48 margin-bottom: 10px;
49 border: 1px solid #ccc;
50 border-radius: 4px;
51}
52
53#task-list-container div button {
54 background-color: #dc3545;
55 border-color: #dc3545;
56}
57
58#task-list-container div button:hover {
59 background-color: #c82333;
60}
Inside the app.js
file, add the following code:
1const express = require("express");
2const bodyParser = require("body-parser");
3const app = express();
4
5app.use(bodyParser.urlencoded({ extended: true }));
6
7let tasks = [];
8
9app.engine("html", require("ejs").renderFile);
10app.set("view engine", "html");
11
12app.set("views", "./views");
13
14app.use(express.static("public"));
15// Serve static files from the public directory
16
17// Render the main view with tasks
18app.get("/", (req, res) => {
19 res.render("index", { tasks });
20});
21
22// Add a new task
23app.post("/add", (req, res) => {
24 const task = req.body.task;
25 if (task) {
26 tasks.push({ id: tasks.length + 1, task: task });
27 }
28 res.render("partials/task_list", { tasks });
29});
30
31// Delete a task
32app.post("/delete/:taskId", (req, res) => {
33 const taskId = parseInt(req.params.taskId, 10);
34 tasks = tasks.filter((task) => task.id !== taskId);
35 res.render("partials/task_list", { tasks });
36});
37
38const port = 3000;
39app.listen(port, () => {
40 console.log(`Server running at http://localhost:${port}/`);
41});
In the code above, we are setting up a basic express server that listens to HTTP requests to manage the task lists. With this, users can add by sending a POST request to the \add
with the task details in the request body. They can also delete a task by sending a POST request to /delete/:taskId/
where :taskId
is the ID of the task to be deleted.
Finally, start your node app:
node app.js
Check the result in the browser:
Below is the link to the code on GitHub.
In this article, we discussed HTMX, what it entails, its features, and provided an example. We also compared it against React to see how it ranked.
Both libraries are a great choice when it comes to building modern applications. Choosing the right one for your project depends entirely on the requirements of your project.
I'm a web developer and writer. I love to share my experiences and things I've learned through writing.