#
Dialogs with Bootstrap
#
Implementing Browser-Style Dialogs with htmx and Bootstrap
The Dialog Browser pattern demonstrates how to replace native browser dialogs like confirm() and prompt() with rich, custom-styled Bootstrap modals using htmx. This allows you to maintain a consistent UI/UX while still leveraging server-side logic for confirmation and input processing.
#
1. Triggering the Modal
In Index.cshtml, we have buttons that request the modal content from the server. Instead of the modal being hidden in the DOM, it is fetched on-demand.
Index.cshtml
<!-- Trigger for Confirm Dialog -->
<button class="btn btn-danger"
hx-get="?handler=ConfirmModal"
hx-target="#modal-container"
hx-trigger="click"
hx-swap="innerHTML">
Delete Something (Confirm)
</button>
<!-- Container where modals are injected -->
<div id="modal-container"></div>
hx-get="?handler=ConfirmModal": Calls the server to get the modal HTML fragment.hx-target="#modal-container": Injects the modal into a dedicated container at the bottom of the page.
#
2. The Modal Fragment (with Hyperscript)
The partial view contains the Bootstrap modal structure. We use a small amount of Hyperscript (the _ attribute) to handle the Bootstrap lifecycle (showing the modal and removing it from the DOM after it's hidden).
_ConfirmModal.cshtml
<div class="modal fade" id="confirmModal" tabindex="-1" role="dialog"
_="on load js(me) new bootstrap.Modal(me).show() end
on htmx:beforeOnLoad from body
if detail.xhr.status >= 200 and detail.xhr.status < 300
js(me) bootstrap.Modal.getInstance(me).hide() end
end
end
on hidden.bs.modal remove me">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirm Deletion</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
Are you sure you want to delete this item?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger"
hx-get="?handler=ConfirmAction"
hx-target="#confirm-result">
Yes, Delete It
</button>
</div>
</div>
</div>
</div>
- Hyperscript logic: On
load, it initializes and shows the Bootstrap modal. Onhtmx:beforeOnLoad(when the "Yes" action finishes successfully), it hides the modal. Onhidden.bs.modal, it cleans up by removing itself from the DOM. hx-get="?handler=ConfirmAction": The actual action performed once the user confirms.
#
3. Handling Prompts
The Prompt pattern is similar but uses a form inside the modal to capture user input.
_PromptModal.cshtml
<form hx-get="?handler=PromptAction" hx-target="#prompt-result">
<div class="modal-body">
<label for="promptValue" class="form-label">Name:</label>
<input type="text" class="form-control" id="promptValue" name="promptValue" required autofocus>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
- When the form is submitted, htmx sends the
promptValueto the server, updates the target on the main page, and the Hyperscript on the wrappingdivhandles closing the modal.
#
4. The Backend: C# PageModel
The Index.cshtml.cs handles returning the partial views for the modals and the final actions triggered from within them.
Index.cshtml.cs
public class Index : PageModel
{
// Returns the Modal HTML
public IActionResult OnGetConfirmModal() => Partial("_ConfirmModal");
// Performs the confirmed action
public IActionResult OnGetConfirmAction()
{
return Content("<div class='alert alert-success'>Successfully confirmed!</div>");
}
// Processes the prompt input
public IActionResult OnGetPromptAction(string promptValue)
{
return Content($"<div class='alert alert-info'>You entered: <strong>{promptValue}</strong></div>");
}
}
#
Why this works well
- UX Consistency: You can style these dialogs to perfectly match your application's theme, unlike native browser dialogs.
- Server-Side Templates: You don't need to write complex JavaScript to build modals or manage their state; the server provides the HTML.
- Automatic Cleanup: Using Hyperscript to
remove meon hidden ensures that your DOM doesn't get cluttered with orphaned modal elements. - Native Fallback: For simple cases, you can still use the built-in
hx-confirm="Message"attribute for a quick native browser confirmation without any extra templates.