Toasts with Django+HTMX
This is a follow-up to my previous article, which showed how to implement a modal form with Django and HTMX. In this article, we’ll see how to add a user notification in the form of a toast.
In the same spirit of my previous article, the solution I present here requires only a few lines of JavaScript and is reusable.
You can find the complete source code for this project on GitHub. I uploaded two versions: one using Bootstrap 4 and the other using Bootstrap 5. You’ll find each version in the corresponding branch. The code snippets in this article use Bootstrap 5.
This article is also available as a YouTube video
Step 1: create the toast element
First, we need the HTML element for the toast. It will be initially hidden and empty.
Place the following lines at the end of <body>
:
<div class="position-fixed top-0 end-0 p-3">
<div id="toast" class="toast align-items-center text-white bg-success border-0" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div id="toast-body" class="toast-body"></div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
</div>
This code is directly taken from Bootstrap’s documentation, except that the toast body is empty.
Notice that I declared the ids toast
and toast-body
. We’ll use them from the JavaScript code.
Step 2: emit a second event from the view
In the previous article, we used the HX-Trigger
header to raise a JavaScript event from the Django view:
return HttpResponse(status=204, headers={'HX-Trigger': 'movieListChanged'})
Now, we’ll change this to raise a second event. HTMX lets us do that with an alternative syntax for the HX-Trigger
header. Instead of a simple string that names the event, we can provide a JSON string containing the names of the events associated with a payload. In our case, we need this JSON string to be:
{
"movieListChanged": null,
"showMessage": "<name of the movie> added."
}
As you can see, the movieListChanged
is still there but doesn’t carry any data.
I added the showMessage
event with the associated message.
Here is the updated code for the view:
return HttpResponse(
status=204,
headers={
'HX-Trigger': json.dumps({
"movieListChanged": None,
"showMessage": f"{movie.title} added."
})
})
Step 3: intercept the event
On the client-side, we must listen for this new showMessage
event a display a toast when it occurs.
Here is the JavaScript code for that:
const toastElement = document.getElementById("toast")
const toastBody = document.getElementById("toast-body")
const toast = new bootstrap.Toast(toastElement, { delay: 2000 })
htmx.on("showMessage", (e) => {
toastBody.innerText = e.detail.value
toast.show()
})
As announced in step 1, we use the ids toast
and toast-body
to locate the toast elements in the DOM.
The third line creates the Toast
object. This is specific to Bootstrap 5; you’ll have to adapt this line to your CSS framework.
In the event handler, we set the toast message from the event’s payload, and finally, show the toast.