HTMX Confirm Dialogs
A confirm dialog asks the user to verify a destructive or important action before the request fires. Without confirmation, a misclick on a Delete button can cause data loss. HTMX provides a built-in confirmation mechanism with the hx-confirm attribute, and it also supports custom dialog patterns for more polished user interfaces.
The Built-In hx-confirm Attribute
The simplest confirmation in HTMX is one attribute:
<button hx-delete="/posts/15" hx-target="#post-15" hx-swap="outerHTML" hx-confirm="Are you sure you want to delete this post? This cannot be undone."> Delete Post </button>
Flow:
User clicks [Delete Post]
|
v
Browser shows native confirm dialog:
┌────────────────────────────────────┐
│ Are you sure you want to delete │
│ this post? This cannot be undone. │
│ │
│ [Cancel] [OK] │
└────────────────────────────────────┘
| |
User clicks Cancel User clicks OK
| |
Nothing happens HTMX sends DELETE /posts/15
|
Server deletes post
|
#post-15 disappears from page
The native browser dialog is accessible, keyboard-navigable, and requires zero CSS. Its main limitation is that it looks different on every browser and cannot be styled.
hx-confirm on Any Element
The hx-confirm attribute works on any element that has an HTMX request attribute:
<!-- On a link --> <a href="#" hx-post="/archive/42" hx-confirm="Archive this item?">Archive</a> <!-- On a form's submit button --> <form hx-post="/transfer-funds" hx-target="#status"> <input type="number" name="amount"> <button type="submit" hx-confirm="Transfer these funds?">Transfer</button> </form>
Custom Confirm Dialog
For a styled, branded dialog, you override HTMX's built-in confirm behavior using the htmx:confirm event. HTMX fires this event before any confirmed request. You intercept it, show your own dialog, and call detail.issueRequest() when the user confirms.
<!-- Custom modal HTML -->
<div id="confirm-modal" style="display:none; position:fixed; top:0; left:0;
width:100%; height:100%; background:rgba(0,0,0,0.5);
align-items:center; justify-content:center;">
<div style="background:#fff; padding:30px; border-radius:8px; max-width:400px;">
<p id="confirm-message"></p>
<button id="confirm-yes">Yes, proceed</button>
<button id="confirm-no">Cancel</button>
</div>
</div>
<!-- Trigger button -->
<button
hx-delete="/accounts/7"
hx-target="#account-7"
hx-swap="outerHTML"
hx-confirm="Close this account permanently?">
Close Account
</button>
<script>
let pendingRequest = null;
htmx.on('htmx:confirm', function(event) {
event.preventDefault(); // Stop HTMX from using the native dialog
pendingRequest = event.detail;
document.getElementById('confirm-message').textContent = event.detail.question;
const modal = document.getElementById('confirm-modal');
modal.style.display = 'flex';
});
document.getElementById('confirm-yes').onclick = function() {
document.getElementById('confirm-modal').style.display = 'none';
if (pendingRequest) {
pendingRequest.issueRequest(true); // Proceed with the HTMX request
pendingRequest = null;
}
};
document.getElementById('confirm-no').onclick = function() {
document.getElementById('confirm-modal').style.display = 'none';
pendingRequest = null;
};
</script>
Custom dialog flow:
User clicks [Close Account]
|
v
htmx:confirm event fires
JavaScript intercepts it, shows custom modal:
┌─────────────────────────────────────────┐
│ Close this account permanently? │
│ │
│ [Yes, proceed] [Cancel] │
└─────────────────────────────────────────┘
"Yes, proceed" → issueRequest(true) → DELETE fires
"Cancel" → modal hides, nothing happens
Two-Step Confirmation With Text Input
For irreversible actions — like deleting an account or purging data — require the user to type a confirmation phrase. This pattern prevents accidental confirmations:
<div id="delete-zone">
<p>Type DELETE to confirm account removal:</p>
<input type="text" id="confirm-text" placeholder="Type DELETE">
<button
hx-delete="/account"
hx-target="body"
hx-swap="innerHTML"
hx-confirm="This will permanently delete your account."
onclick="return checkConfirm()">
Delete My Account
</button>
</div>
<script>
function checkConfirm() {
if (document.getElementById('confirm-text').value !== 'DELETE') {
alert('Please type DELETE to confirm.');
return false; // Prevent HTMX from firing
}
return true;
}
</script>
Best Practices for Confirm Dialogs
| Guideline | Why |
|---|---|
| Use plain, specific language | "Delete this comment?" is clearer than "Confirm action" |
| Put the destructive action on the right | Users read left to right; Cancel on the left prevents misclicks |
| Make the Cancel button prominent | Encourages caution; the safe path should be easy to take |
| Do not overuse confirmations | Confirmation fatigue causes users to click OK without reading |
| Reserve for irreversible actions only | Actions with an undo option do not need a confirm dialog |
Confirm for Bulk Actions
<button hx-post="/posts/delete-all" hx-target="#post-list" hx-swap="innerHTML" hx-confirm="Delete all 47 posts? This cannot be undone."> Delete All Posts </button>
Including the item count in the confirmation message ("all 47 posts") makes users pause and think before confirming. It is a small detail that prevents a lot of support tickets.
Key Takeaway
Use hx-confirm to add a native browser confirmation dialog to any HTMX request. For a styled custom dialog, listen to the htmx:confirm event in JavaScript, show your own modal, and call detail.issueRequest(true) when the user confirms. Reserve confirm dialogs for truly destructive or irreversible actions. For the most dangerous operations, require the user to type a confirmation phrase before proceeding.
