Logo

Show Django flash messages as toasts with Htmx

Learn how to display Django messages as user friendly toasts with Htmx

Published: 11 Oct, 2024


Django has an amazing messages framework that you can use to notify users about various things that require their attention.

After setting up the framework, you simply them in templates using whatever layout you want. I usually rely on Bootstrap’s Alert component to display these messages. However, I decided that Toasts are more user friendly because they will subtly pop-up and dismiss themselves after some time.

In this post, I’ll show you how I implemented messages as toasts using Htmx in one of my projects.

Workflow

I will have a simple view that will render a template containing HTML representing the current messages available in the form of Bootstrap toasts. This is what a typically toast looks like:

{% if messages %}
{% for message in messages %}
<div
  class="toast align-items-center"
  role="alert"
  aria-live="assertive"
  aria-atomic="true"
>
  <div class="d-flex">
    <div class="toast-body">{{ message|safe }}</div>
    <button
      type="button"
      class="btn-close me-2 m-auto"
      data-bs-dismiss="toast"
      aria-label="Close"
    ></button>
  </div>
</div>
{% endfor %}
{% endif %}

I’ll iterate over the list of messages and output HTML for each toast with the messages content in the body.

The view is going to be only two lines:

def toast_messages(request):
    return render(request, "common/toast_messages.html")

I’m going to use Htmx to issues a GET request to this endpoint and dump the contents in the DOM. Here’s how to do it:

<div hx-get="{% url "core:toast-messages" %}" hx-trigger="htmx:afterRequest" hx-swap="afterbegin" class="toast-container top-0 end-0 p-3">
    {% include "common/toast_messages.html" %}
</div>

When the page loads, I want to render any messages available. Then, after every Htmx request, I want to fetch messages again and dump them here as toasts. This is great for displaying toasts after AJAX requests that don’t trigger a page refresh.

Customizing toasts

The toast component above is bare bones. I want to show different kinds of toasts depending on the message leveral (or severity). For example, I want the toast to have a red background for messages with level of error. I’m going to use custom template tags to implement those.

Dump the following in a file called toast_tags.py in one of your app’s templatetags directory.

from django import template

register = template.Library()


@register.simple_tag
def get_toast_color_scheme(toast_tag):
    if toast_tag == "info":
        return "text-bg-info"
    elif toast_tag == "success":
        return "text-bg-success"
    elif toast_tag == "warning":
        return "text-bg-warning"
    elif toast_tag == "danger":
        return "text-bg-danger"
    return ""


@register.simple_tag
def get_toast_close_button_color(toast_tag):
    if toast_tag == "info":
        return "btn-close-dark"
    elif toast_tag == "success":
        return "btn-close-white"
    elif toast_tag == "warning":
        return "btn-close-dark"
    elif toast_tag == "danger":
        return "btn-close-white"
    return ""

When the background color changes, I also need to change the color of the close button to make it visible.

Now, update your toast_messages.html file like so:

{% load toast_tags %}
{% if messages %}
{% for message in messages %}
<div
  class="toast align-items-center {% get_toast_color_scheme message.level_tag %}"
  role="alert"
  aria-live="assertive"
  aria-atomic="true"
>
  <div class="d-flex">
    <div class="toast-body">{{ message|safe }}</div>
    <button
      type="button"
      class="btn-close me-2 m-auto {% get_toast_close_button_color message.level_tag %}"
      data-bs-dismiss="toast"
      aria-label="Close"
    ></button>
  </div>
</div>
{% endfor %}
{% endif %}

Now the toast will be properly formatted according to the level of the message.

Conclusion

Django and Htmx make it simple to notify users of important messages via toasts. All you have to do is render the HTML, and then use Htmx to fetch any new messages after every AJAX request. Try it out in your project now.


Email me if you have any questions about this post.

Subscribe to the RSS feed to keep updated.