Welcome to Django Htmx Modal Forms documentation!

Django Htmx Modal Forms

CI Status Documentation Status Test coverage percentage

uv Ruff pre-commit

PyPI Version Supported Python versions License


Documentation: https://django-htmx-modal-forms.readthedocs.io

Source Code: https://github.com/abe-101/django-htmx-modal-forms


A Django package that provides class-based views for handling forms in Bootstrap modals using HTMX. This package makes it easy to add create and update functionality to your Django models with a clean modal interface.

Features

  • 🚀 Easy-to-use class-based views for modal forms

  • ⚡ HTMX-powered for dynamic updates without page reloads

  • 🎨 Bootstrap modal integration with customizable sizes

  • ✨ Automatic form validation with error handling

  • 🐛 Debug mode for development

Requirements

  • Python 3.8+

  • Django 4.2+

  • django-htmx

  • Bootstrap 5

  • django-crispy-forms

Installation

  1. Install via pip:

pip install django-htmx-modal-forms
  1. Add to your INSTALLED_APPS:

INSTALLED_APPS = [
    ...
    "crispy_forms",
    "crispy_bootstrap5",
    "django-htmx"
    "django_htmx_modal_forms",
]
  1. Load and include the JavaScript in your base template:

{% load htmx_modal_forms %}

<!doctype html>
<html>
  <head>
    <!-- Required dependencies -->
    <script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
    <script src="{% static 'js/htmx.min.js' %}"></script>

    <!-- Modal handlers -->
    {% htmx_modal_script %}
  </head>
  <body>
    <!-- Your content -->
  </body>
</html>

Quick Start

  1. Create your view:

from django_htmx_modal_forms import HtmxModalUpdateView

class PersonUpdateView(HtmxModalUpdateView):
    model = Person
    form_class = PersonForm
    detail_template_name = "persons/_person_card.html"
    modal_size = "lg"  # Optional: sm, lg, or xl
  1. Add the URL pattern:

path("persons/<int:pk>/edit/", PersonUpdateView.as_view(), name="person_update"),
  1. Create your detail template (_person_card.html):

<div id="person-{{ person.id }}" class="card">
  <div class="card-body">
    <h5 class="card-title">{{ person.name }}</h5>
    <p class="card-text">{{ person.email }}</p>
  </div>
</div>

Important: The wrapper element must have an ID that matches your model instance (e.g., id="person-{{ person.id }}")! This ID is used by the view to locate and replace the content after a successful form submission.

  1. Add a button to trigger the modal:

<button
  hx-get="{% url 'person_update' pk=person.pk %}"
  hx-target="body"
  hx-swap="beforeend"
  class="btn btn-primary"
>
  Edit Person
</button>

That’s it! When you click the edit button, a modal will appear with your form. On successful submission, the person’s card will automatically update with the new information.

Advanced Usage

Custom Modal Titles

class PersonCreateView(HtmxModalCreateView):
    model = Person
    form_class = PersonForm
    modal_title = "Add New Team Member"  # Custom title

Different Modal Sizes

class PersonUpdateView(HtmxModalUpdateView):
    model = Person
    form_class = PersonForm
    modal_size = "xl"  # Available: sm, lg, xl

Debug Mode

Debug mode is automatically enabled when settings.DEBUG = True. It provides helpful console logging for:

  • Modal initialization

  • Event triggers

  • Bootstrap/HTMX availability

  • Error conditions

How It Works Behind the Scenes

The package orchestrates a series of interactions between Django, HTMX, and Bootstrap:

  1. When you click an edit button, HTMX makes a GET request to your view

  2. The view returns a Bootstrap modal containing your form and triggers the modal:show event

  3. The included JavaScript initializes and displays the modal

  4. When submitting the form:

    • If there are validation errors, the view replaces the form content with the errors

    • On success:

      1. The view updates your model

      2. Renders the new content using your detail template

      3. Uses HTMX’s out-of-band swap to replace the content using the ID you provided

      4. Triggers the modal to close

This approach provides a smooth user experience with minimal JavaScript while maintaining Django’s server-side validation and template rendering.

[Previous content remains the same until Contributing section]

Credits

This package was inspired by Josh Karamuth’s blog posts on Django + HTMX modals:

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. To develop locally:

  1. Clone the repository

  2. Install dependencies: uv sync

  3. Run tests: uv run pytest

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Abe Hanoka
Abe Hanoka

💻 🤔 📖

This project follows the all-contributors specification. Contributions of any kind welcome!

Credits

This package was created with Copier and the browniebroke/pypackage-template project template.