Modal

A dialog with a backdrop, Escape to close, focus return and body scroll lock.

Use Modal for a confirmation, a short form or any content that needs the customer's attention before continuing. For a side panel, use Drawer.

Example

app/components/cart/ConfirmClear.tsx
import { useState } from 'react';
import { Modal } from '@salla.sa/twilight-theme-engine/modal';

export function ConfirmClear({ onConfirm }: { onConfirm: () => void }) {
  const [open, setOpen] = useState(false);
  return (
    <>
      <button onClick={() => setOpen(true)}>Clear cart</button>
      <Modal isOpen={open} onClose={() => setOpen(false)} size="sm">
        <Modal.Header onClose={() => setOpen(false)}>Clear the cart?</Modal.Header>
        <Modal.Body>All items will be removed.</Modal.Body>
        <Modal.Footer>
          <button onClick={() => setOpen(false)}>Cancel</button>
          <button onClick={onConfirm}>Clear</button>
        </Modal.Footer>
      </Modal>
    </>
  );
}

You own the open state: the modal only asks you to close it through onClose.

API

PropTypeDescription
isOpenbooleanWhether the modal is shown. Required.
onClose() => voidCalled on Escape or backdrop click. You set isOpen to false. Required.
childrenReactNodeModal content. Required.
size?"sm" | "md" | "lg" | "xl" | "full"Maximum width. Default: "md".
position?"center" | "top"Vertical placement. Default: "center".
closeOnBackdropClick?booleanClose when the backdrop is clicked. Default: true.
closeOnEscape?booleanClose on the Escape key. Default: true.
preventBodyScroll?booleanLock page scroll while open. Default: true.
className?stringClass on the modal panel.

Parts: Modal.Header, Modal.Body and Modal.Footer. Each accepts normal div attributes. Modal.Header also takes showCloseButton (default true) and onClose.

Gotchas

  • The close button in Modal.Header appears only when you pass onClose to the header too.
  • Always give the customer a visible way to close it (the header button or your own). Do not rely on a backdrop click alone.
  • Clicking, typing and pressing Enter or Space inside the panel never close it, so forms and buttons inside work as usual. Only a backdrop click and Escape call onClose.
  • It renders nothing while closed and during server rendering, and uses a portal when open.
  • While open it adds the class menu-opened to <body>, and on close focus returns to the element that had it before.
  • ImageModal is also exported from this path and loads lazily.

Next

On this page