Logo

How to show a modal in Django + HTMX

Learn how to show a modal with minimal Javascript in Django + HTMX

Published: 16 Dec, 2024


Showing modals with HTMX is dead easy thanks to the ability to trigger client side events from your backend.

In this post, I’ll show you how to leverage this power to show a Bootstrap modal in your Django app.

See this Github project for the full example.

Take this example HTML page:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Django + Htmx modal</title>
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>

<body>

  <h1>Django + HTMX Modal</h1>

  <button type="button" hx-get="{% url 'modal' %}" hx-swap="beforeend" hx-target="body"
    class="btn btn-primary btn-lg">Show Modal</button>

  <script src="https://unpkg.com/[email protected]"
    integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+"
    crossorigin="anonymous"></script>

  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
    crossorigin="anonymous"></script>
</body>

</html>

Notice the button that will issue a GET request to the designated URL and then, Dump the response body before the closing </body> tag.

Cool, here’s the view that will be served at the URL specified:

from django.shortcuts import render
from django_htmx.http import trigger_client_event

def modal_view(request):
    response = render(request, "modal.html")
    return trigger_client_event(response, "modal:show", after="swap")

Here you will render a template and then ask HTMX to trigger an event named modal:show after it inserts the content in the DOM. Here’s what the rendered template can look like:

<div class="modal fade" id="modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <p>This is the modal body</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>

Boostrap modals are opt-in. You need to initialize them before they’ll show up in the UI. Add the following Javascript on any page that will contain a modal (You can put it in your base layout).

    document.addEventListener("modal:show", () => {
      const modalEl = document.getElementById("modal");
      const bsModal = window.bootstrap.Modal.getOrCreateInstance(modalEl);
      bsModal.show();
      modalEl.addEventListener("hidden.bs.modal", () => {
        modalEl.remove();
      });
    });

As soon as the modal:show event is dispatched, we’ll initialize the modal and show it. Once the modal is dismissed by the user, we’ll remove it from the DOM. This ensures that there can be only a single modal on any page, which removes any fears of conflict.

Go ahead and click the Show Modal button now and you’ll see that the modal shows up. Next, dismiss the modal, then inspect the DOM using your developer tools. You’ll see that it’s gone.


Email me if you have any questions about this post.

Subscribe to the RSS feed to keep updated.