Skip to content

Modal

Trigger button + dialog overlay. Content is loaded via HTMX into the dialog body.

from htmforge.components import Modal

Modal(
    modal_id="confirm",
    trigger_label="Open",
    hx_url="/modal/content",
    close_label="Close",
).to_html()

Props

Name Type Default Notes
modal_id str required id for the <dialog> and body container
trigger_label str required label on the trigger button
hx_url str required URL loaded into the modal body
hx_target str "" overrides the default body target when set
close_label str Close label for the close button

Rendered HTML

<div class="modal-wrapper"><button type="button" data-modal-target="confirm" class="modal-trigger" hx-get="/modal/content" hx-target="#confirm-body" hx-swap="innerHTML">Open</button><dialog id="confirm" class="modal"><div id="confirm-body" class="modal-body"></div><form method="dialog"><button class="modal-close">Close</button></form></dialog><script>if(!window.__htmforgeModalDelegated){window.__htmforgeModalDelegated=true;document.addEventListener('click',function(e){var btn=e.target.closest('[data-modal-target]');if(!btn)return;var dlg=document.getElementById(btn.getAttribute('data-modal-target'));if(dlg&&!dlg.open)dlg.showModal();});}</script></div>

Multiple modals on one page: the delegated click listener is registered exactly once (guarded by window.__htmforgeModalDelegated), regardless of how many Modal instances render their own <script> tag. Each trigger click is matched via closest('[data-modal-target]') and showModal() is only called when the dialog isn't already open, so two or more modals on the same page never double-fire or throw InvalidStateError (see #16).