What is HTMX? Tutorial and practical examples

Published on June 25, 2025

what-is-htmx-header1

HTMX is a lightweight JavaScript library for building server-driven front ends by extending HTML with custom attributes. In contrast to other frameworks that recreate the existing app with their own syntax, HTMX provides a simpler grammar for building front ends by modifying the HTML of your pages with attributes rather than replacement components.

This guide explains what HTMX is, how it works, and why developers choose to use it. It also includes a tutorial with example code to get you started building your own HTMX apps.

What is HTMX?

HTMX is a single, frontend library that tries to avoid the need for you to learn a new architecture ideology by extending HTML itself. HTMX takes the approach that HTML is essentially incomplete, from the markup side of things. The core idea is to make HTML complete by providing a behind-the-scenes behavior to fill in where you might otherwise expect HTML to provide certain things. The behavior is integrated into your page via new attributes on the standard HTML elements.

Since it is just a single library, HTMX can also sit alongside other frameworks, meaning you can use it to add some light, partial updates to a portion of your existing application. Since you don’t need to learn many behaviors for interactivity, this makes reading and maintaining the client side logic much easier.

The biggest difference to other frontend options is to return HTML from your back end, rather than JSON or XML, etc. Once you get into the pattern, it’s actually quite easy to move forward, as you can store much of your page content as HTML template files — either partially or as whole pages.

HTMX allows you to render your HTML content into a specific element on the page in a controlled manner. Other popular frameworks do this by completely reinventing the frontend architecture and requiring you to break your application into components, which you need to learn as a separate library or discipline. HTMX attempts to make this part of its expanded HTML specification.

The documentation is also extensive and very complete, making examples easy to read, build, or implement when you’re preparing to extend your code.

HTMX Tutorial: Building a full-stack app with HTMX and Node.js

You can clone our GitHub repository at /simple-htmx-node-backend to get up and running with this tutorial code immediately.

This tutorial shows you how to build a simple HTMX app project, containing a list of pages that each demonstrate one practical example powered by HTMX. This will use Node.js as the back end. The key difference to server-side rendered apps, or building apps that rely on REST APIs for their backend, is that you will need to return HTML rather than JSON or XML. Many examples are available from the HTMX project documentation.

Because HTMX requires no Node.JS build process for your frontend application, you can host your HTMX front end on static hosting for improved performance and cost effectiveness, then host your back end separately.

Creating a HTMX project

To get started, install Node.js.

Now, create a new folder for your HTMX app and navigate to it in your command line.

Next, initialize NPM in your project folder:

npm init -y

Install the Express package:

npm install express

Create the file server.js and add this starter code to it:

Create a folder public and in there add the file index.html and add this initial content to it:

You now have a basic HTML web page. To add HTMX, simply add this script element to the <head> element of your page:

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

Run your app with:

node server.js

Open this page in your browser: http://localhost:3000/

You should now see the text “HTMX with Node.js examples” on a plain page.

The examples that follow will all use the index.html and server.js files you just created; however, in the repository, you will find them under their own pages. For this tutorial, simply add the code to the existing files (the basic setup) and they will work.

Basic content loading with hx-get

Most HTML elements have so-called “natural” events, such as the <form> element’s submit event. HTMX extends this model by extending these triggers both for any element and in any manner. For example, adding hx-get to a DIV element allows the DIV to trigger the request, not just form submission elements like buttons.

Add this HTML to the index.html file, right inside the <body>...</body> element:

The button element is specifying that the code should make a GET request to the /load-data URL, and whatever is returned will replace the contents of the parent DIV element, as defined by the hx-get, hx-target, and hx-swap attributes. 

To see this in action, add this code to the end of the server.js file, and restart your Express app:

This is the simplest request listener for your Node.js server, which serves the static DIV content. 

what-is-htmx-image1

Click the button and you’ll see the text in the button replaced with the text “New content loaded!”

Form submission without page reloading with hx-post

Once you have your page designed and know where served content will be placed, you’ll want to design the requests themselves. The page makes a simple GET request to the server at the URL relative to the current page. The request types supported by HTMX are:

  • hx-get

  • hx-post

  • hx-put

  • hx-patch

  • hx-delete

As with any HTML form, the values passed in the body message of the request will be the values of any field elements in the form.

Remove the code you just added to the two files above (index.html and server.js) and put this HTML into the index.html file:

This is specifying that the form submission will make a post request to the server and use whatever comes back to replace the entire outer DIV element.

Now, add this JavaScript to the server.js file and restart your server again:

Refresh the page, enter your name into the text field, and click Submit. You will see that the server’s response completely replaces the content you just added to the page. This response includes the text you entered into the INPUT element (or “Guest” if you entered nothing).

Infinite scrolling with hx-trigger

Remove the recently added HTML and JavaScript again and add this into the <body> element of your index.html file:

This is a little more complex. First, we have an item-container, which will have any loaded content appended to the elements inside it. We can do this with the hx-target and hx-swap attributes.

The first content load is triggered by the hx-trigger attribute specifying that the request should be made when the page initially completes loading in the browser.

You can modify triggers so that they only trigger under certain conditions or get limited in particular ways.

Now we have the server.js to drop in:

This code defines some dummy content — just 1,000 text items labeled “Item X.”

The GET listener gets the page index from the URL parameters, works out the page start and end from the page size (in this case 10, but it could be anything, including specified on the URL), and grabs a chunk of the items from the dummy content array.

It then renders (server-side) some HTML to be served and, if we’re not yet on the last page of dummy items, adds on an extra DIV element containing another hx-trigger attribute. When scrolled into view, this final DIV will cause the next page of content to load.

Again, restart your Express server, then refresh the browser and you will see content being loaded dynamically onto the page as you scroll down.

Search suggestions with hx-swap

Auto-completion is a great feature to have, and HTMX makes it easy to implement. Drop this simple form onto your your index.html page, replacing the previous content:

By now, this should be easy to read and pretty familiar, demonstrating just how little HTMX you actually need to get useful things built.

You’re making a GET request to the /suggest endpoint, but this time you’re waiting for the keyup event to fire when the user releases a keyboard key in the INPUT element. When this happens, a 300 millisecond delay occurs. If another key is not pressed in that time and the content of the INPUT element has actually changed, HTMX makes a request to get content. The response is updated in the suggestions DIV.

This Node.js logic will return the auto-complete suggestions:

This code simply takes the query parameter and searches in the dummy list of items for a match. It returns anything found as a simple list of text items containing DIV elements.

For this one, you can check the repository for a little CSS to make the suggestion list more appealing, but only a little.

Tab navigation with hx-target

When creating an HTML page that will receive server-side content, the first step is to determine where that content will be placed and how it will be placed into — or how it will replace — content on the page. This is called targeting and swapping.

The default target for any request made by HTMX is the current element and the default swap is innerHTML. This means that if you provide no alternative and use a button to request content, the returned content will replace the contents of the button, not the button element itself.

For tabbed navigation items, we’ll need this CSS dropped into the header of the index.html file:

Now, add the tabs and the placeholder for their content to the <body> element:

Again, this is quite simple, but we’re using three buttons to target the same DIV element’s inner content with the HTML content loaded from the server at the unique tab’s URL. Here’s the logic to return that content for server.js:

Of course, this is actually three separate request endpoints to serve each tab uniquely. The content for each is similarly simple, but it could be anything you want, including full HTML files containing more HTMX.

There are also a few HTMX-only CSS Selectors you can play around with, including:

  • this – The same as not providing a target, it simply targets the element executing the request.

  • closest <selector> – Finds the immediate parent element of the one executing the request, as long as the <selector> matches that element.

  • find <selector> – Finds the first child within the element executing the request, as long as the <selector> matches it.

In-place editing with hx-put

Finally, a little more styling to replace the previous example’s CSS in the index.html file:

Now, replace the content dropped into the <body> element with this DIV:

Here, the page calls the server when the DIV is clicked like a button. The content returned replaces the DIV text content. Let’s see what the server returns.

This is the most complex server-side part yet. It assumes the default username “User123”  and returns a new FORM element.

The form specifies a PUT request to another server endpoint, /update-username, instead of the one just called. This shows you how dynamic the server-driven logic can be. As this is a PUT request, it could also be defined under the same URL, but let’s not get carried away.

The form returned then shows an INPUT element with the username and a button that lets you save that username back to the server.

Here’s the next event listener for the second endpoint address in this example. Drop this after the /edit-username event listener above.

The endpoint logic takes the username provided by the user in the previously returned INPUT element and serves a simple DIV with that name. There’s nothing flashy going on here, so don’t be concerned when clicking the DIV again and it appears to forget the entered username — we just haven’t coded it up to remember that.

Advanced HTMX functionality

The content above should get you in the right mindset for learning HTMX, and while we would love to cover everything HTMX has to offer in detail, it offers a truly huge number of capabilities.

Here are some areas to note when looking to improve your base implementation:

  • Indicators and UX enhancements: Placing the special CSS class htmx-indicator on an element hides that element until a request is made. This lets you design user interfaces that show the user something while completing a potentially long request.

  • Polling and special events: The hx-trigger attribute lets you specify the conditions under which the client should make a request. You can also delay a request in seconds and milliseconds. You can trigger a request only when an element is scrolled into view on the page, and you can cause the client to repeat a request every given number of seconds.

  • Boost, navigation, and history: hx-boost is a boolean attribute that will tell HTMX to make an AJAX request to the given URL but replace the entire page without navigating away from the current URL. This effectively hides the navigation. hx-push-url is another boolean attribute that tells HTMX to add the request URL to the browser history, where hx-push-url controls whether a URL goes into the history and hx-replace-url updates the current URL in the browser history.

Using HTMX with Contentful

The learning curve with HTMX is shallow and the library is nimble enough that you can begin adding HTMX to your existing project or begin building a Contentful-powered HTMX web app from scratch.

HTMX supports extensions, one of which is the json-enc extension, which lets your HTMX front end access your Contentful content, allowing you to find your content without any additional friction.

Because HTMX friction with other libraries and frameworks is so low, you can use it alongside the Contentful JS SDK and still leverage third-party SaaS offerings to reduce the development load by providing prebuilt, tested, and customizable functionality.

Subscribe for updates

Build better digital experiences with Contentful updates direct to your inbox.

Meet the authors

David Fateh

David Fateh

Software Engineer

Contentful

David Fateh is a software engineer with a penchant for web development. He helped build the Contentful App Framework and now works with developers that want to take advantage of it.

Related articles

Grid of mint green Python programming language logos with three different colored icons breaking the pattern in the middle row
Guides

FastAPI vs. Flask: Python web frameworks comparison and tutorial

June 12, 2025

Learn React routing with this comprehensive guide! Discover key concepts, explore tools like React Router, and see how to build navigation for your React apps.
Guides

Mastering React routing: A guide to routing in React

January 20, 2025

Ready to deploy Content Source Maps in a Next.js project? Enhance live previews with automatic links between displayed content and corresponding fields in Contentful.
Guides

Effortless automatic content linking in Next.js using Content Source Maps

September 30, 2024

Contentful Logo 2.5 Dark

Ready to start building?

Put everything you learned into action. Create and publish your content with Contentful — no credit card required.

Get started