Modal
Portal-rendered dialog overlay with backdrop, escape key, and scroll lock. Five sizes plus a confirm preset.
Modal is the canonical dialog overlay. Portal-rendered (escapes the source DOM), backdrop with optional click-to-close, Escape key dismissal, scroll lock on the underlying page. Five sizes plus a preset="confirm" for destructive/confirmation flows.
Use it for
- Focused tasks (forms, multi-step flows)
- Confirmations and destructive-action prompts (
preset="confirm") - Any modal interaction where the user needs to commit or cancel before continuing
For sliding panels anchored to a screen edge (detail views, edit workflows), use Sheet. For floating content panels anchored to a trigger, use Popover.
Import
import { Modal } from '@brikdesigns/bds';Default mode
import { useState } from 'react';
const [open, setOpen] = useState(false);
<Modal
isOpen={open}
onClose={() => setOpen(false)}
title="Edit profile"
footer={
<>
<Button variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
<Button variant="primary" onClick={handleSave}>Save</Button>
</>
}
>
<p>Form content here…</p>
</Modal>Sizes
| Size | Use case |
|---|---|
sm | Compact messages, simple confirms |
md (default) | Standard dialogs |
lg | Extended content |
xl | Data-heavy views |
full | Full-screen takeover |
Confirm preset
preset="confirm" collapses the legacy Dialog component into Modal. Sets role="alertdialog", locks to a tighter 440px width, hides the X close button, and auto-renders Cancel / Confirm buttons.
<Modal
isOpen={open}
onClose={() => setOpen(false)}
preset="confirm"
title="Delete this item?"
description="This action cannot be undone."
confirmLabel="Delete"
confirmVariant="destructive"
onConfirm={handleDelete}
/>Confirm preset props
confirmLabel/cancelLabel— button labels (defaults: "Confirm" / "Cancel")confirmVariant—'primary'(default) or'destructive'for delete-style actionsconfirmDisabled— disables the confirm button (e.g. while form is invalid)confirmLoading— shows loading state on the confirm button (in-flight request)onConfirm— called when the user confirms
Pass a custom footer to override the auto-rendered actions while keeping the rest of the preset.
The standalone Dialog component is @deprecated and slated for deletion in a future major version. Migrate to Modal preset="confirm" — same prop names, same behavior. See ADR-004.
When not to use
- Don't use Modal for persistent state. Modals interrupt the user. For background notifications, use Toast. For persistent alerts, use Banner.
- Don't use Modal for ephemeral hover content. Use Tooltip or Popover.
- Don't nest Modals. Modal-on-modal indicates a flow problem. Refactor as a single multi-step Modal or as a Sheet push-stack.
- Don't put navigation in a Modal. Modals are for committing — back-out flows belong on the page itself.
Accessibility
- Renders
role="dialog"(oralertdialogfor confirm preset) witharia-labelledbylinking the title. - Focus trap inside the modal —
Tabcycles within, focus returns to the trigger on close. Escapecloses (auto). Backdrop click closes by default; opt out viacloseOnBackdrop={false}.- Body scroll is locked while the modal is open.
API
Default mode
| Prop | Type | Default |
|---|---|---|
isOpen | boolean (required) | — |
onClose | () => void (required) | — |
title | string | — |
size | 'sm' | 'md' | 'lg' | 'xl' | 'full' | 'md' |
children | ReactNode | — |
footer | ReactNode | — |
closeOnBackdrop | boolean | true |
closeOnEscape | boolean | true |
showCloseButton | boolean | true |
Confirm preset
| Prop | Type | Default |
|---|---|---|
preset | 'confirm' (required) | — |
title | string (required) | — |
description | string | — |
confirmLabel | string | 'Confirm' |
cancelLabel | string | 'Cancel' |
confirmVariant | 'primary' | 'destructive' | 'primary' |
confirmDisabled | boolean | false |
confirmLoading | boolean | false |
onConfirm | () => void | — |
Related
- Dialog (deprecated) — migrate to
Modal preset="confirm" - Sheet — sliding panel alternative
- Popover — anchored-to-trigger alternative
- Banner — persistent state alerts
- Storybook playground