Skip to content

Modal

A dialog that opens on top of the page and blocks it until closed. Use it for focused tasks and confirmations; for supporting content next to the trigger, use Popover.

Code
demo.tsx
import {CloseIcon} from '@earendil-works/elanor-icons-react';
import {Button, IconButton, Modal, Stack} from '@earendil-works/elanor-react';

export default function ModalDemo() {
  return (
    <Modal.Root>
      <Modal.Trigger render={<Button hierarchy="secondary" />}>Rename project</Modal.Trigger>
      <Modal.Portal>
        <Modal.Backdrop />
        <Modal.Viewport>
          <Modal.Popup>
            <Stack direction="row" align="start" gap="auto">
              <Modal.Title>Rename project</Modal.Title>
              <Modal.Close render={<IconButton aria-label="Close" />}>
                <CloseIcon />
              </Modal.Close>
            </Stack>
            <Modal.Description>Members see the new name right away.</Modal.Description>
            <Modal.Body>The project URL stays the same, so existing links keep working.</Modal.Body>
            <Modal.Actions>
              <Modal.Close render={<Button />}>Save</Modal.Close>
              <Modal.Close render={<Button hierarchy="secondary" />}>Cancel</Modal.Close>
            </Modal.Actions>
          </Modal.Popup>
        </Modal.Viewport>
      </Modal.Portal>
    </Modal.Root>
  );
}

Usage

import {Modal} from '@earendil-works/elanor-react';

<Modal.Root>
  <Modal.Trigger />
  <Modal.Portal>
    <Modal.Backdrop />
    <Modal.Viewport>
      <Modal.Popup>
        <Modal.Title />
        <Modal.Description />
        <Modal.Body />
        <Modal.Actions>
          <Modal.Close />
        </Modal.Actions>
      </Modal.Popup>
    </Modal.Viewport>
  </Modal.Portal>
</Modal.Root>;

The modal moves focus inside, keeps it there, and returns it to the trigger on close. Escape and clicking the backdrop close it.

Modal.Close closes the modal; render it as a Button or an Icon button. Title and Description name and describe the dialog for screen readers.

Static rendering

On pages rendered on the server without React in the browser, use the static parts. Body and Actions stay the same. Bind the behavior once the page has loaded:

<Modal.StaticRoot>
  <Modal.StaticTrigger />
  <Modal.StaticPopup>
    <Modal.StaticTitle />
    <Modal.StaticDescription />
    <Modal.Body />
    <Modal.Actions>
      <Modal.StaticClose />
    </Modal.Actions>
  </Modal.StaticPopup>
</Modal.StaticRoot>
import {createModal, enhance} from '@earendil-works/elanor-react';

enhance({modal: createModal});

The popup is a native modal dialog: the browser traps focus, makes the page inert, and returns focus when it closes. Its buttons open and close it through invoker commands, also without the behavior. The behavior animates opening and closing like the React parts and closes it on Escape and on a press on the backdrop. To close it from a script, call the dialog’s requestClose(), which closes it the same way as Escape. Give StaticRoot an id to open the dialog from other buttons too, with commandfor and command="show-modal".

API

Modal.Root

Accepts Base UI Dialog.Root props.

Modal.Trigger

Accepts Base UI Dialog.Trigger props.

Modal.Portal

Accepts Base UI Dialog.Portal props.

Modal.Backdrop

Accepts Base UI Dialog.Backdrop props.

Modal.Viewport

Accepts Base UI Dialog.Viewport props.

Modal.Popup

Accepts Base UI Dialog.Popup props.

Modal.Title

Accepts Base UI Dialog.Title props.

Modal.Description

Accepts Base UI Dialog.Description props.

Modal.Body

Renders <div>. Accepts its HTML attributes and ref.

Modal.Actions

Renders <div>. Accepts its HTML attributes and ref.

Modal.Close

Accepts Base UI Dialog.Close props.

Modal.StaticRoot

A modal for pages without React in the browser; createModal makes it work. Renders no element.

PropTypeDefaultDescription
idstring-The dialog's id, so other buttons can open it with commandfor. Generated by default.
childrenReactNode-

Also accepts HTML attributes and ref.

Modal.StaticTrigger

Renders <button>. Accepts its HTML attributes and ref.

Modal.StaticPopup

The dialog, centered over the page's backdrop.

PropTypeDefaultDescription
data-elanorstring-The page's own behaviors for this element, attached after the component's.

Renders <dialog>. Also accepts its HTML attributes and ref.

Modal.StaticTitle

Renders <h2>. Accepts its HTML attributes and ref.

Modal.StaticDescription

Renders <p>. Accepts its HTML attributes and ref.

Modal.StaticClose

Renders <button>. Accepts its HTML attributes and ref.