HTMX: Server-Driven Interactivity#

One of the core technologies FastHTML builds on is htmx. HTMX extends the capabilities of HTML to allow for server-driven interactivity. This is in contrast to client-side interactivity, which is handled by Javascript.

HTMX gives two main capabilities that we do not get from HTML alone:

  1. The ability to trigger any kind of HTTP request from any element on a page

  2. The ability to update any element on a page with the response from any request, without having to reload the page

Traditionally, these capabilities have required web developers to write Javascript to handle page updates. HTMX, especially in the context of FastHTML, allows us to use Python to handle page updates.

Add a form to our hello, world app#

%%writefile app_3.py

from fasthtml.common import *

app, rt = fast_app()

def create_greeting_form():
    return Form(
        Input(type="text", name="name", placeholder="Enter your name"),
        Button("Submit", type="submit"),
        hx_post="/greet",
        hx_target="#greeting",
        hx_swap="innerHTML"
    )

@rt("/")
def get():
    return Titled("FastHTML",
        Div(
            P("Hello, World!"),
            create_greeting_form(),
            Div(id="greeting-message"),
            id="greeting"
        )
    )

@rt("/greet", methods=["POST"])
def greet(name: str):
    return Div(
        P(f"Hello, {name}!"),
        create_greeting_form(),
        Div(id="greeting-message"),
        id="greeting"
    )

serve()
Overwriting app_3.py

We have updated our simple “hello, world” app to include a form that allows the user to enter their name and submit it to the server. The server then replaces “Hello, World!” with a Hello to whatever name was submitted. And it does this without reloading the entire page.

There are a few new things here:

  1. This is our first use of an example that takes user input, via the Form component. If you look at the documentation, you will see that Form can take a number of different hx_* attributes. These are various HTMX attributes that control the behavior of the form submission.

  2. On that note—we used several hx_* attributes in our form. In particular:

  • hx_post tells HTMX to send an asynchronous POST request when the form is submitted, allowing for partial page updates without a full page reload.

  • hx_target tells HTMX to update the element with the id greeting with the response from the server.

  • hx_swap tells HTMX to replace the contents of the element with the id greeting with the response from the server. Specifically, innerHTML means that the response from the server will be inserted into the element, replacing whatever was there before.

  1. This is the first time we have used a POST request. In this case, the request is sent to the server with the form data. The server then returns a response, which is used to update the page. GET requests, which we have seen in previous examples, are typically used to retrieve data from the server. POST requests are typically used to send data to the server and get back a response.

Add HTMX to arbitrary elements#

One of the benefits of HTMX is that it allows us to add interactivity to arbitrary elements on a page. This is in contrast to HTML alone, which is limited to interactivity that is triggered by form submissions and link clicks and must reload the entire page to update the content of the page.

Let’s demonstrate this by changging the color of “Hello, World” to red whenever the user mouses over it.

%%writefile app_3.py

from fasthtml.common import *

app, rt = fast_app()

@rt("/")
def get():
    return Titled("FastHTML", 
        P("Hello, World!", 
          hx_get="/change-color", 
          hx_trigger="mouseenter",
          hx_swap="outerHTML"
        )
    )

@rt("/change-color")
def get():
    return P("Hello, World!", 
             style="color: red;",
             hx_get="/revert-color",
             hx_trigger="mouseleave",
             hx_swap="outerHTML"
            )

@rt("/revert-color")
def get():
    return P("Hello, World!", 
             hx_get="/change-color", 
             hx_trigger="mouseenter",
             hx_swap="outerHTML"
            )

serve()
Overwriting app_3.py

What is happening here?

  1. When the user mouses over “Hello, World!”, the server sends a request to /change-color.

  2. The server returns a response with the updated content, which includes the hx_get attribute. This tells HTMX to send a request to /revert-color when the user mouses out of the element.

  3. The server returns a response with the updated content, which includes the hx_get attribute. This tells HTMX to send a request to /change-color when the user mouses over the element again.

  4. This cycle repeats indefinitely, causing the color of the element to change whenever the user mouses over it and revert to the original color whenever the user mouses out of it.

You’ll notice that much of the behavior of HTMX attributes is determined by the hx_trigger and hx_swap attributes. Knowing where to look for the available options is important! Here is one source of events that can be used as triggers, and here are the available options for swapping.

Summary#

This has been a brief introduction to HTMX in FastHTML. We have only scratched the surface of what is possible with HTMX. For more information, please see the HTMX documentation.