Skip to content

useDialog

Edit on GitHub
@volvo-cars/react-headless v0.24.7

Manages native HTML dialog elements in React (MDN dialog docs).

import { useDialog } from '@volvo-cars/react-headless';
const MyComponent = () => {
const { dialogProps, showDialog, closeDialog } = useDialog();
return (
<>
<button onClick={showDialog}>Open dialog</button>
<dialog className="dialog-large" {...dialogProps}>
<article slot="main">
<h2>Dialog title</h2>
<p>Dialog content</p>
</article>
<footer slot="footer">
<button onClick={closeDialog}>Close</button>
</footer>
</dialog>
</>
);
};

The state of the dialog can be controlled by passing open and onToggle:

import { useState } from 'react';
import { useDialog } from '@volvo-cars/react-headless';
function ControlledDialog() {
const [open, setOpen] = useState(false);
const { dialogProps, showDialog, closeDialog } = useDialog({
open,
onToggle: ({ open, reason }) => {
setOpen(open);
console.log('Dialog closed with reason:', reason);
},
});
return (
<>
<button onClick={showDialog}>Open dialog</button>
<dialog className="dialog-large" {...dialogProps}>
<article slot="main">
<p>Controlled dialog content</p>
</article>
<footer slot="footer">
<button onClick={() => closeDialog('confirm')}>Confirm</button>
<button onClick={() => closeDialog('cancel')}>Cancel</button>
</footer>
</dialog>
</>
);
}

Prevent the dialog from being dismissed by backdrop click or Escape key:

import { useDialog } from '@volvo-cars/react-headless';
function NonDismissibleDialog() {
const { dialogProps, showDialog, closeDialog } = useDialog({
dismissible: false,
});
return (
<>
<button onClick={showDialog}>Open dialog</button>
<dialog className="dialog-large" {...dialogProps}>
<article slot="main">
<p>You must click a button to close this dialog.</p>
</article>
<footer slot="footer">
<button onClick={closeDialog}>Close</button>
</footer>
</dialog>
</>
);
}

Use onBeforeDismiss to intercept and optionally prevent dismissal:

import { useDialog } from '@volvo-cars/react-headless';
function ConfirmBeforeClose() {
const { dialogProps, showDialog } = useDialog({
onBeforeDismiss: (e) => {
if (!confirm('Are you sure you want to close?')) {
e.preventDefault();
}
},
});
return (
<>
<button onClick={showDialog}>Open dialog</button>
<dialog className="dialog-large" {...dialogProps}>
<article slot="main">
<p>Try to close by clicking the backdrop or pressing Escape.</p>
</article>
</dialog>
</>
);
}

useDialog accepts an optional options object:

  • open — controlled open state. Makes the dialog a controlled component.
  • onToggle — callback called when the dialog is opened or closed. Receives { target, open, reason }.
  • dismissible — whether the dialog can be dismissed by clicking the backdrop or pressing Escape (default: true)
  • resetScroll — whether to reset scroll position of all scrollable children when opening (default: true)
  • ref — optional existing ref to the dialog element. If not provided, one will be created.
  • onBeforeDismiss — callback that runs before the dialog is dismissed. Call e.preventDefault() to block dismissal.

The hook returns an object with:

  • dialogProps — props to spread on the <dialog> element
  • showDialog() — opens the dialog as a modal. Returns a promise that resolves when animations finish.
  • closeDialog(returnValue?) — closes the dialog, optionally setting the return value. Returns a promise that resolves when animations finish.