Hone UI
Components

Dialog

A modal window for confirmations, short forms and decisions.

Dialog opens a modal over the page. Build it from Dialog, DialogTitle, DialogContent, DialogContentText and DialogActions. It sits on React Aria's modal overlay, so focus moves in, Tab stays inside, the page behind is hidden from screen readers and scrolling locks.

import Dialog from '@hone-ui/react/Dialog';
import DialogTitle from '@hone-ui/react/DialogTitle';
import DialogContent from '@hone-ui/react/DialogContent';
import DialogContentText from '@hone-ui/react/DialogContentText';
import DialogActions from '@hone-ui/react/DialogActions';

Basic dialog

You own the open state. The dialog never closes by itself, so set it to false in onClose.

const [open, setOpen] = useState(false);

<Dialog open={open} onClose={() => setOpen(false)}>
  <DialogTitle>Delete file?</DialogTitle>
  <DialogContent>
    <DialogContentText>This cannot be undone.</DialogContentText>
  </DialogContent>
  <DialogActions>
    <Button onClick={() => setOpen(false)}>Cancel</Button>
    <Button onClick={remove}>Delete</Button>
  </DialogActions>
</Dialog>;

Force a choice

Ignore backdrop clicks so the dialog closes only through its buttons.

<Dialog
  open={open}
  onClose={(event, reason) => {
    if (reason !== 'backdropClick') setOpen(false);
  }}
>
  ...
</Dialog>

Size and scrolling

<Dialog open={open} onClose={close} fullWidth maxWidth="md">...</Dialog>
<Dialog open={open} onClose={close} fullScreen>...</Dialog>
<Dialog open={open} onClose={close} scroll="body">...</Dialog>

Dialog props

PropTypeDefaultDescription
openbooleanrequiredWhether the dialog is visible.
onClose(event, reason) => voidnoneCalled on Escape or an outside press. reason is 'backdropClick' or 'escapeKeyDown'.
fullWidthbooleanfalseGrows the paper to maxWidth.
maxWidth'xs', 'sm', 'md', 'lg', 'xl', false'sm'444, 600, 900, 1200 or 1536px.
fullScreenbooleanfalseFills the viewport with square corners.
scroll'paper', 'body''paper'Scroll DialogContent inside the paper, or the whole dialog.
disableEscapeKeyDownbooleanfalseMakes Escape do nothing.
hideBackdropbooleanfalseHides the dimmed layer.
PaperProps, slotProps.paperobjectnoneProps for the Paper surface. slotProps.paper wins. The default elevation is 24. See Form dialog.
slotProps.backdropobjectnoneProps for the Backdrop, such as invisible.
aria-labelledby, aria-describedbystringnoneLand on the element with role="dialog".
sx, className, style, ref, id, childrenStandard. They land on the root layer that holds the backdrop and paper.

Part props

ComponentProps
DialogTitlechildren, sx, className, style, ref and every HTML prop of an h2.
DialogContentdividers (boolean, default false) adds borders above and below with even padding. Plus the standard props.
DialogContentTextEvery Typography prop. variant defaults to 'body1', color to 'text.secondary' and component to 'p'.
DialogActionsdisableSpacing (boolean, default false) removes the gap between actions. Plus the standard props.

Form dialog

The paper is always a section. Set component: 'form' and Dialog renders a form inside it, around the title, content and actions. onSubmit, onReset, action, method, noValidate, encType, target, autoComplete, name and acceptCharset go to the form, and nothing else does. className, sx, style, elevation, aria-* and every other prop stay on the paper section.

<Dialog
  open={open}
  onClose={close}
  slotProps={{ paper: { component: 'form', onSubmit: handleSubmit } }}
>
  <DialogTitle>Subscribe</DialogTitle>
  <DialogContent>...</DialogContent>
  <DialogActions>
    <Button type="submit">Subscribe</Button>
  </DialogActions>
</Dialog>

Notes

  • onClose receives the real DOM Event, not a React SyntheticEvent. React Aria drops the event, so Dialog reads window.event inside the synchronous dismiss callback. If no event is running, it skips onClose instead of inventing one. The event is not cancelable.
  • Not supported are keepMounted, TransitionComponent, transitionDuration, TransitionProps, slots, container, disableScrollLock, disableAutoFocus, disableEnforceFocus and disableRestoreFocus. Focus trap, scroll lock and focus return always run.
  • DialogContent, DialogContentText and DialogActions are Server Components and ignore theme defaultProps. Dialog and DialogTitle read them.
  • Enter and exit are CSS fades. They drop to none under prefers-reduced-motion.

Accessibility

A Dialog needs an accessible name. A DialogTitle supplies one with no id. Without a title, set aria-label or aria-labelledby on the Dialog. To describe it with body text, give DialogContentText an id and pass that id as aria-describedby. Keep the Dialog mounted and toggle open, so the exit fade runs. Put the safe action first in DialogActions and the main action last.

On this page