HTMX

HTMX Might Be a Big Deal for WordPress

A brief overview of what HTMX is, how it challenges the we way we build modern user interfaces on the web, and the potential impact it could have on WordPress plugin development.
Table of Contents

Sponsored Ad

If you purchase through a link on our site, we may earn a commission.

Building a rich user experience in the browser can be a challenging task, one that often requires a significant amount of JavaScript code. As the needs and ambitions of our application grows, so does the complexity of our JavaScript code. It is therefore no surprise how often developers have revised how we think about, and write, JavaScript apps.

Days since last JS framework.

WordPress plugin developers have it worse. The environment that we target is neither a server that we control, nor one with complete control over the entire page. While it’s very possible to use a JavaScript framework successfully in a WordPress plugin, it can also be remarkably easy to end up with a project whose scale and complexity is beyond what you intended or expected.

But what if it didn’t need to be this way? In this article, we’ll be exploring how modern web UIs are built with JavaScript, the difficulties that developers face, and the alternative offered by HTMX. In particular, we’ll be taking a look at why HTMX and WordPress may be a match made in heaven.

How we got here

Before JavaScript, browsers were basically just glorified document readers. As such, most experiences on the web were ‘multi-page applications’, or MPAs for short. MPAs are web applications that consist of multiple HTML documents, one for each page in the application. As the user uses the application, they are shown different documents with different available actions.

MPAs are very straightforward to build. Navigation is done using <a> tags to link to other documents, and user input can be captured with a <form> element. The server responds to link and form requests with new HTML documents, replacing the page that is being shown on the screen.

A good example of a MPA that you are likely very familiar with is WP Admin. Each admin page is a document with HTML that is generated by the WordPress PHP code that is running on the server. Most pages in WP Admin, like the WordPress settings pages, tend to be mostly comprised of forms that you, the user, can submit.

Conversely, a single-page application, or SPA, is an application that uses a single HTML page. Navigation and user input is then handled by JavaScript code, dynamically changing parts of the page in-place without needing to swap out the entire page or refresh it. This results in a smoother and more responsive user experience.

These days, many web applications use SPAs for their web interface. At RebelCode, we’ve built SPAs for the admin interfaces of our two main WordPress plugins: Spotlight and Aggregator. The relatively new site editor in WordPress is also a SPA, as is the block-based post editor.

The price we pay

SPAs are their own applications, written in JavaScript and running in the browser. Their own definition is also their biggest caveat: they do not have immediate access to server resources. This means that we need to establish a channel of communication between the SPA and the server.

Let’s create a simple WordPress plugin to use an example. This plugin provides
a simple CRUD user interface for managing books. The plugin’s internal API on the server may look something like this:

<?php

function get_books(?int $count = null, int $page = 1): Book[];
function get_book(int $id): Book;
function insert_book(Book $book): Book;
function update_book(Book $book): Book;
function delete_book(int $id): void;

To create our modern SPA interface, we’ll use a JavaScript framework like React; the most popular JavaScript framework that is also used by WordPress. Let’s start by adding an admin page:

<?php

add_action('admin_menu', function () {
    add_menu_page('Books', 'Books', 'manage_options', 'books', 'books_page');
});

function books_page() {
    echo '<div id="books-app"></div>';
}

Our page will render a single, empty <div> element which will serve as the root of our React application where the rest of the UI will be rendered.

const rootEl = document.getElementById("books-app");
const root = ReactDOM.createRoot(rootEl);
root.render(<BooksApp />);

function BooksApp() {
  return (
    <div>
      <h1>My Books</h1>
      ...
    </div>
  );
}

So how do we list the books that are stored in the database? The code to do that is on the server, so we need a way to call it and get its result.

To do that, we can expose a JSON API from the server. The React app can then make a request at our API’s URL, receive the books in JSON format, then render the list. For this example, let’s assume that we’ve added an endpoint to the WordPress REST API:

GET 

{
  "books": [
    {
      "id": 15,
      "title": "Mistborn",
      "author": "Brandon Sanderson",
    },
    {
      "id": 44,
      "title": "The Hobbit",
      "author": "J. R. R. Tolkien",
    },
  ]
}

We can then write a React component that fetches the books and renders them as a list:

function BookList() {
  const [books, setBooks] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(
    function () {
      setIsLoading(true);
      fetch("https://my-wp-site.com/wp-json/books")
        .then((res) => res.json())
        .then((data) => setBooks(data.books))
        .else((error) => setError(error))
        .finally(() => setIsLoading(false));
    },
    [setBooks, setIsLoading],
  );

  if (isLoading) {
    return <div>Loading...</div>;
  }
  if (error) {
    return <div>Error: {error}</div>;
  }

  return (
    <ul>
      <li>
        {books.map((book) => (
          <a key={book.id} href={book.url}>
            {book.title}
          </a>
        ))}
      </li>
    </ul>
  );
}

But this solution is too naive, and yields a rough user experience. It does not cater for state changes after the component un-mounts, caching the response, retrying failed queries, or prevent stale state from overwriting more recent state. In fact, the way we are using fetch() in a React effect is generally discouraged.

In many ways, this can be worse than a traditional MPA. So to do this correctly, we’ll need to implement a few more things in our client. Or, more realistically, use 3rd party packages.

But all of this is starting to feel like a disproportionate amount of effort just to render a list of books. Do we really need to create a JavaScript app and a JSON API to create a smooth user experience?

Let’s contrast this with an MPA, where rendering the list of books can be accomplished in just a few lines of PHP code, without any dependencies:

<?php

function render_books() {
    ?>
    <ul>
        <?php foreach (get_books() as $book): ?>
            <li>
                <a href="<?= $book->url ?>">
                    <?= $book->title ?>
                </a>
            </li>
        <?php endforeach; ?>
    </ul>
    <?php
}

But of course, this is not a fair comparison. This list of books is just static HTML; it is not reactive to state changes or user input.

If we want to have a SPA-like experience while also rendering the HTML on the server, where our code has immediate access to the database, we’ll need to find a way to have the server-rendered HTML find its way to the browser and replace the previous list of books. But achieving this without any JavaScript code is currently impossible, so we’d have to bite the bullet and use JavaScript anyway.

But we don’t need to write it ourselves.

Introducing HTMX

HTMX is a small JavaScript library that primarily does one thing: allow HTML to request new HTML from the server. It does this using new attributes, which allow us to tell HTMX where to get the new HTML from, what to swap it out with, and what triggers the swapping. It acts as a bridge between our HTML server and the page in the browser.

This is a vastly different way of thinking about SPAs, since we are not building a client JavaScript application to update the current page. Instead, we simply add some HTML attributes to tell HTMX how we want the page to change when certain events happen.

Even without HTMX, you can already change what is shown on the screen using just HTML, albeit in a very limited way. You’re already familiar with this HTML feature: the humble <a> link element.

<a href="https://my-wp-site.com/books">View books</a>

A link element gives the browser all of the information that is necessary is carry out navigation. When it is clicked, the browser takes the href from the element, makes a request at that URL, downloads the response and, assuming that it contained HTML, replaces the contents of the page with the new HTML.

The <form> element is another example of how HTML can request new HTML.

<form action="/contact.php">
  <label>
    Your message:
    <input type="text" name="message" />
  </label>
  <button type="submit">Send message</button>
</form>

This time, the browser collects the values from all the inputs in the form, send them to the server, downloads the response, and renders it on the screen.

Why should only <a> and <form> be able to make HTTP requests? Why should you only be able to replace the entire screen?

From HTMX’s GitHub readme

Well, HTMX changes that.

<a href="https://my-wp-site.com/books" hx-target="#books">
  View books
</a>
<div id="books"></div>

With the HTMX hx-target attribute, clicking the link will now place the response from inside the element with the "books" ID. Of course, embedding a page inside of another is not the goal here. Our server does not need to response with the full page, and can instead just respond with an HTML fragment.

By exposing HTML fragments from our server and telling HTMX how, from where, and when to get those fragments, we can create a SPA-like web application without any JavaScript, where the server is in full control. In a sense, HTML has become our new JSON.

And all we need to do is load the HTMX script into our page:

<script src="https://unpkg.com/htmx.org@1.9.12"></script>

(Be sure to check out the HTMX documentation for instructions, as the above code may be out-of-date).

Let’s look at another example:

<button hx-get="/button/off" hx-target="this" hx-swap="outerHTML">
  Turn off
</button>

There’s a lot more going on here so let’s break it down:

  • hx-get specifies the URL to send a GET request to when the button is clicked.
  • hx-target="this" tells HTMX to swap the clicked button with the response.
  • hx-swap="outerHTML" tells HTMX to swap out the entire button, not just what’s inside it.

All together, this tells HTMX:

When the button is clicked, send a GET request to /button/off and replace this button with the response.

Let’s say the server responds to /button/off with the below HTML:

<button hx-get="/button/on" hx-target="this" hx-swap="outerHTML">
  Turn on
</button>

Can you see the difference? The hx-get attribute now points to /button/on and the text inside the button is now “Turn on”. When this button is clicked, it will also be replaced with the response from /button/on. As you can imagine, we can have the server respond with the original button to complete our toggle!

This simple idea of allowing any element to request new HTML from the server and decide where that HTML goes turns out to be quite powerful. We can create tabs, search with live results, progress bars, and more.

Pros and Cons

Unlike most JavaScript frameworks, HTMX does not require our client application code to be compiled and bundled. This alone is huge benefit; JavaScript build systems can be notoriously difficult to set up and maintain, especially as you start to introduce more exotic features and libraries, like TypeScript, JSX, CSS pre-processors, etc. It’s not uncommon for a medium-to-large teams to have one or more members dedicated to this task.

Another obvious benefit is the lack of a separate client application. Since all we need is an HTTP server that responds with HTML, we can use any programming language that we like. This may be a big selling point for you if your team lacks familiarity with modern JavaScript, or is not big enough to justify building two applications. It can be especially tempting if you’re a WordPress plugin developer, since you could use PHP for all aspects of your plugin.

But perhaps the most important benefit is that you no longer need an API between the back-end and front-end of your application. This can save a tremendous amount of development time, as well as reduce the amount of code that can produce bugs, which also saves time in the long-term.

However, we shouldn’t be so naive as to assume that using HTMX means not having to write any JavaScript. Some amount of JavaScript may still be required for things like dragging-and-dropping, charts, color and date pickers, and so on. Though we can always use framework-agnostic solutions, such as SortableJS and Floating UI. Additionally, we may also find less need for JavaScript in the future as web standards continue to evolve with new HTML elements, like the recent <dialog> element.

Secondaly, PHP is ironically not very good at HTML templating, despite it being built to do just that. Its tag syntax is overly verbose, and its HEREDOC string syntax has limited support for string interpolation.

Lastly, creating an endpoint in WordPress is not very straightforward. Consider the books plugin from the previous examples. We need to have a path on the server that responds with the list of books in HTML form. How do we regiser this endpoint?

There are many options, but none make it as simple as it ought to be.

HATEOAS

There is a subtle detail in our previous example that is very easy to miss, and it will likely sound obvious once it has been pointed out.

When we get the HTML from the server, the button on the page is either the ON variant or the OFF variant. Depending on which one is shown on screen, the click action will be different.

Because of this, the browser does not need to understand our application. We would normally make the browser understand by giving JavaScript code to explicitly program in all the behaviors. Now, we only have HTML, and the browser needs no prior knowledge of how our application behaves or what its state is. It just needs to render the HTML on the screen, which itself encodes the state our application.

This type of architecture is known as HATEOAS, which stands for ‘Hypermedia As The Engine Of Application State’. It is a specialized type of REST architecture that uses hypermedia as the medium for state transfer, and that same hypermedia becomes the interface through which the user drives the application into new states.

The HTMX website has a great collection of articles, essays, and talks on this subject, if you’re interested in learning more. For this purposes of this article, let’s move on to why HTMX could be a big deal for WordPress developers.

HTMX <3 WordPress

WordPress is a giant, monolithic PHP server. WordPress plugins are also primarily written in PHP. They can add new functionality to the site using PHP APIs that are provided by WordPress, such as the Hooks API and the Database API. These APIs are not available from JavaScript, so plugin developers should want to keep as much of their plugin code on the server as possible. If there was ever a motivation to use HTMX, this is it!

In many ways, HTMX was built for WordPress. Or rather, for applications like WordPress; applications that would rather not be burdened with a foreign language that forces them to leave behind their collections of server APIs. Especially not when simply transferring state using hypermedia would be sufficient.

Making it easier to create good UIs for WordPress plugins can have a dramatic impact on the plugin ecosystem. Developers that maintain free plugins may find it more feasible to build better user experiences for their users, and smaller teams may be able to iterate faster on features with the time saved. This can help make smaller plugins more competitive in a market that is heavily dominated by big teams with even bigger budgets.

Larger plugins may also be especially interested. JavaScript apps can grow exponentially fast. HTMX could allow these plugins to remove their massive JSON APIs and JavaScript apps, and leave a lean HTML server in their place that has full access to the WordPress APIs.

Final Thoughts

I’ve been played around with HTMX for a while now, using it with PHP and Go. It offers a compelling alternative for building user interfaces on the web, and a convincing argument for using hypermedia to drive application state.

If you’re a plugin developer, be sure to check out HTMX. We barely scratched the surface in this article, and the documentation is very well written with plenty of examples. It’s also surprisingly short, given how much HTMX comes with out of the box. You should be able to get started with HTMX and PHP in a few minutes.

Miguel is a passionate software developer who loves to tinker, reverse-engineer, and learn new skills. He has spent the past 10 years working with RebelCode to build amazing WordPress plugins that serve thousands of users worldwide.

Sponsored Ad

If you purchase through a link on our site, we may earn a commission.

All suggestions are anonymous.

More from our blog...

2 Responses

  1. About performance? Did you got a opportunity to make some tests and benchmark with other Front-end framework like Astro or something?

    1. I did not. I imagine it wouldn’t be a fair comparison though, given how small the overlap is between HTMX and other JS frameworks. What aspects of HTMX would you want to see benchmarked?

Post a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Stay updated with WP Mayor's newsletter showcase every week

Stay on top of every new WordPress innovation and latest launches. Receive all our fresh product reviews and expert guides directly in your inbox.

Hosting Survey 2024

Are you happy with your hosting provider or are you over-paying for too little? Have your say below!

"*" indicates required fields

What's the main reason you picked this host?*
How happy are you with your host?*

OPTIONAL: If you'd like to receive the results of this survey, please enter your details below.