<Modal>

Creates a modal dialog.

This follows the pattern in ARIA Authoring Practices guidelines

This is a <Dialog> wrapped in an element to mask the rest of the page. Additional code ensures the focus is contained within the modal.

Multiple modals can be opened, but only the top-most modal is active.

While a modal is open the rest of the document is inert. The inert polyfill is used to set the content to inert.

Modals use React portal to place them at the end of the document. This ensures local positioning does not interfere with the modal positioning and that the reset of the document can be set to inert.

Usage

const [open, setOpen] = useState(false);
<button
  type="button"
  aria-haspopup="dialog"
  aria-expanded={open}
  onClick={() => setOpen(!open)}
>
  Show
</button>

{open && (
  <Modal title="Title" onClose={() => setOpen(false)}>
    <p>
      Modal contents
    </p>
  </Dialog>
)}

Props

The props are mostly the same as a <Dialog>.

Prop Type Default Purpose
actions Node Add additional content before the close button
active Boolean true If set to false, the dialog is considered not active and will not close on Escape
arrow Node Adds content above the header. Intended for an error when positioning.
bodyClassName String "react-dialogs-active-modal" This class is added to the <body> when the modal is active
children Node Dialog contents
className String null Class name of dialog
classPrefix String "react-dialogs-modal" Class prefix for BEM classes
focus Boolean true Set false to disable focusing the dialog when rendered
id String Random value The id of the dialog
onClose Function Called when the dialog is closed. If omitted the close button will not be rendered and Esc will not close the dialog
title Node The dialog title

Customisation

A number of props are provided to customise the appearance of the component.

The nameProps props allow you to add your own attributes to each part, potentially overriding those already present. This is a good way to add your own classes.

The NameComponent props allow you to replace or override each component. Pass a lower-case string to change the html element, or a full component if you want a more far reaching change. Bear-in-mind you will have to forwardRef the DialogComponent component.

<ModalComponent className={`${classPrefix} ${classPrefix}--active`}                              // <div>
  <DialogComponent className={`${classPrefix}__dialog ${className}`} {...dialogProps}>           // <div>
    {arrow}                                                                                      // null
    <HeaderComponent className={`${classPrefix}__header`} {...headerProps} />                    // <div>
      <TitleComponent className={`${classPrefix}__title`} {...titleProps} />                     // <h2>
      {actions}                                                                                  // null
      <CloseButtonComponent className={`${classPrefix}__close-button`} {...closeButtonProps} />  // <button>
    </HeaderComponent>
    <BodyComponent className={`${classPrefix}__body`} {...bodyProps} />                          // <div>
      {children}
    </BodyComponent>
  </DialogComponent>
</ModalComponent>

Styling

A bodyClassName is added to the body when a modal is active. This allows scrolling to be prevented while the modal is open.