Modal

A dialog that renders in a portal over the page, traps focus while open, and closes on Escape or a backdrop click. Composed from ModalHeader, ModalTitle, ModalBody, and ModalFooter.

Import

tsx
import {
Modal,
ModalHeader,
ModalTitle,
ModalBody,
ModalFooter,
} from '@metatoy/bootstrap-styled';

Launch a modal

Modal is controlled — you own the show state and pass onHide to close it. The button below toggles that state.

The full controlled pattern:

tsx
import { useState } from 'react';
import { Modal, ModalHeader, ModalTitle, ModalBody, ModalFooter, Button, CloseButton } from '@metatoy/bootstrap-styled';

function Example() {
const [show, setShow] = useState(false);
return (
  <>
    <Button onClick={() => setShow(true)}>Open modal</Button>
    <Modal show={show} onHide={() => setShow(false)} centered>
      <ModalHeader>
        <ModalTitle>Modal title</ModalTitle>
        <CloseButton onClick={() => setShow(false)} />
      </ModalHeader>
      <ModalBody>Modal content.</ModalBody>
      <ModalFooter>
        <Button variant="secondary" onClick={() => setShow(false)}>Close</Button>
        <Button onClick={() => setShow(false)}>Save changes</Button>
      </ModalFooter>
    </Modal>
  </>
);
}

Overlay scope. The open modal renders through a portal and its backdrop covers the entire viewport (not just this demo card) — that is the real component behavior. Press Escape or click the dimmed area to dismiss it.

Dialog surface

The Modal* parts are plain styled elements, so you can render the dialog surface inline (without the overlay) to preview its structure:

Sizes, centering & static backdrop

size="sm" | "lg" | "xl" sets the dialog max-width, centered vertically centers it, and staticBackdrop keeps it open when the backdrop is clicked.

Props

PropTypeDefaultDescription
showboolean
onHide() => void
size'sm' | 'lg' | 'xl'
centeredboolean
staticBackdropbooleanDon't close on backdrop click.
childrenReact.ReactNode

The subcomponents — ModalHeader, ModalTitle, ModalBody, ModalFooter — are styled elements that accept all native attributes for their tag (<div> / <h5>).

Theming

The dialog surface reads the global surface tokens rather than a per-component namespace: --bs-body-bg and --bs-body-color for the fill, plus --bs-border-color-translucent, --bs-border-radius-lg, and --bs-box-shadow for the frame. Header and footer dividers use --bs-border-color. Override any of these on an ancestor to re-skin the dialog; because the modal is portaled, the library re-applies the active theme’s variables inside the portal so your tokens still reach it.

Accessibility

The dialog is rendered with aria-modal="true" and a dialog role, and focus is trapped inside while it is open (and restored to the trigger on close). Provide a ModalTitle and wire a CloseButton (which ships an accessible label) to onHide. Escape closes the modal unless you set staticBackdrop.

Open in Ladle

Explore the source component’s story in the library workshop: Modal · Ladle