Skip to main content

Installation

yarn add @twilio-paste/modal

Usage

import { Modal, ModalHeader, ModalHeading, ModalBody, ModalFooter, ModalFooterActions } from '@twilio-paste/modal';
import { Button } from '@twilio-paste/button';
import { useUID } from '@twilio-paste/uid-library';

const MyModal = () => {
  const [isOpen, setIsOpen] = React.useState(false);
  const modalHeadingID = useUID();

  return (
    <>
      <Button variant="primary" onClick={() => setIsOpen(true)}>
        Open Modal
      </Button>
      <Modal
        ariaLabelledby={modalHeadingID}
        isOpen={isOpen}
        onDismiss={() => setIsOpen(false)}
        size="default"
      >
        <ModalHeader>
          <ModalHeading as="h3" id={modalHeadingID}>
            Modal Heading
          </ModalHeading>
        </ModalHeader>
        <ModalBody>
          <p>Modal body content goes here.</p>
        </ModalBody>
        <ModalFooter>
          <ModalFooterActions>
            <Button variant="secondary" onClick={() => setIsOpen(false)}>
              Cancel
            </Button>
            <Button variant="primary">Submit</Button>
          </ModalFooterActions>
        </ModalFooter>
      </Modal>
    </>
  );
};

Props

PropTypeDefaultDescription
ariaLabelledbystring-Required. Accessible title for the Modal. Must match the ID of the ModalHeading.
isOpenboolean-Required. Determines the state of the Modal.
onDismiss() => void-Required. Function to call when the user hits “Escape” or clicks outside the dialog.
size"default" | "wide"-Required. Control whether the Modal is default width or wide.
allowPinchZoombooleantrueHandle zoom/pinch gestures on iOS devices when scroll locking is enabled.
initialFocusRefReact.RefObject<any>-By default the first focusable element will receive focus when the Modal opens. Provide a ref to focus a different element instead.
elementstring"MODAL"Overrides the default element name for custom styling with the Customization Provider.

ModalHeader

PropTypeDefaultDescription
childrenReact.ReactNode-Required. Content to render in the header, typically a ModalHeading.
i18nDismissLabelstring"Close modal"Accessible text for the close button.
elementstring"MODAL_HEADER"Overrides the default element name for custom styling.

ModalHeading

PropTypeDefaultDescription
childrenReact.ReactNode-Required. The heading text.
asHeadingProps["as"]-Required. The HTML heading level (h1, h2, h3, etc.).
idstring-Required. ID that matches the ariaLabelledby prop on Modal.
elementstring"MODAL_HEADING"Overrides the default element name for custom styling.

ModalBody

PropTypeDefaultDescription
childrenReact.ReactNode-Required. The main content of the modal.
elementstring"MODAL_BODY"Overrides the default element name for custom styling.

ModalFooter

PropTypeDefaultDescription
childrenReact.ReactNode-Required. Content to render in the footer, typically ModalFooterActions.
elementstring"MODAL_FOOTER"Overrides the default element name for custom styling.

ModalFooterActions

PropTypeDefaultDescription
childrenReact.ReactNode-Required. Action buttons for the modal.
justify"start" | "end""end"Alignment of the action buttons.
elementstring"MODAL_FOOTER_ACTIONS"Overrides the default element name for custom styling.

Examples

Wide Modal

<Modal
  ariaLabelledby={modalHeadingID}
  isOpen={isOpen}
  onDismiss={handleClose}
  size="wide"
>
  {/* ... */}
</Modal>

Custom Initial Focus

const nameInputRef = React.createRef();

<Modal
  ariaLabelledby={modalHeadingID}
  isOpen={isOpen}
  onDismiss={handleClose}
  initialFocusRef={nameInputRef}
  size="default"
>
  <ModalBody>
    <Input ref={nameInputRef} />
  </ModalBody>
</Modal>
<ModalFooter>
  <ModalFooterActions justify="start">
    <Button variant="secondary">Back</Button>
  </ModalFooterActions>
  <ModalFooterActions>
    <Button variant="secondary" onClick={handleClose}>
      Cancel
    </Button>
    <Button variant="primary">Submit</Button>
  </ModalFooterActions>
</ModalFooter>

Accessibility

  • Modal uses role="dialog" and aria-modal="true"
  • Focus is trapped within the modal when open
  • Pressing Escape closes the modal (unless allowPinchZoom is false)
  • Clicking outside the modal closes it
  • The first focusable element receives focus by default (customizable with initialFocusRef)
  • The close button in the header is keyboard accessible

Build docs developers (and LLMs) love