Demo
Focus-trapped modal dialog with Escape handling, optional backdrop, and scoped open/close events.
Delete item
Are you sure you want to delete this item? This action cannot be undone.
Alpine.js Dialog
huxDialog handles open/close state, optional backdrop behavior, and scoped lifecycle events for modal UI in Alpine.js. Pair it with your own dialog markup and focus trap directives.
This pattern expects the Alpine Focus plugin when you use x-trap in your markup: @alpinejs/focus.
API
huxDialog(config)
Returns an Alpine data object with:
isOpen: booleandialogId: string | nullisPersistent: booleanisSeamless: booleandialogTitleId: string | nullcloseOnBackdrop: booleancloseOnEscape: booleanshowBackdrop: booleanopenDialog(): voidtoggleDialog(): voidcloseDialog(): voidhandleBackdropClick(): voidhandleEscape(): void
Internal helper methods are private implementation details and are not part of the supported API contract.
Options
dialogId: string(optional)startsOpen: boolean(default:false)isPersistent: boolean(default:false)isSeamless: boolean(default:false)
Quick Start
<div x-data="huxDialog({ dialogId: 'settings' })">
<button type="button" x-on:click="openDialog()">Open dialog</button>
<template x-teleport="body">
<div x-cloak x-show="isOpen" x-on:keydown.escape.stop="handleEscape()">
<div
x-cloak
x-show="showBackdrop"
aria-hidden="true"
x-on:click="handleBackdropClick()"
></div>
<div
x-trap.noscroll="isOpen"
role="dialog"
aria-modal="true"
x-bind:aria-labelledby="dialogTitleId || null"
>
<h2 x-bind:id="dialogTitleId || null">Settings</h2>
<button type="button" x-on:click="closeDialog()">Close</button>
</div>
</div>
</template>
</div>Common Usage Patterns
Persistent Confirmation Dialog
huxDialog({
dialogId: 'confirm-delete',
isPersistent: true,
})Seamless Dialog (No Backdrop)
huxDialog({
dialogId: 'quick-actions',
isSeamless: true,
})Behavior Contract
init()derivesdialogTitleIdfromdialogIdas${dialogId}-title, otherwisenull.openDialog()setsisOpen = trueand dispatches open event(s).closeDialog()setsisOpen = falseand dispatches close event(s).toggleDialog()flips open state by delegating toopenDialog()andcloseDialog().isPersistentdisables both Escape and backdrop close via computed flags.isSeamlesshides backdrop viashowBackdrop.- Event names are scoped when
dialogIdexists.
Error Handling
- Dialog methods are defensive and do not throw for repeated calls (
openDialog()while open,closeDialog()while closed). - Unknown event listeners or missing external handlers do not block internal state transitions.
Accessibility Notes
- Keep modal container with
role="dialog"andaria-modal="true". - Provide a visible or screen-reader title and wire
aria-labelledbyto that element. - Keep close controls as labeled
buttonelements withtype="button". - Use keyboard escape handling (
x-on:keydown.escape.stop) and focus trapping (x-trap) so keyboard users remain inside the modal while open.
Notes
- Events emitted:
hux-dialog:{dialogId}:openhux-dialog:{dialogId}:closehux-dialog:openandhux-dialog:closewhendialogIdis missing