Modal
Accessible modal dialog built on Radix Dialog, with an overlay, focus trap, and composable header, icon, body, and footer slots.
Description
Modal is a compound dialog built on Radix Dialog. It renders a focus-trapping overlay that interrupts the page until it is resolved, composed from a root plus sub-parts — ModalTrigger, ModalContent, ModalHeader, ModalIcon, ModalHeaderContent, ModalTitle, ModalDescription, ModalMain, ModalBody, ModalFooter, and ModalClose — so you assemble exactly the layout you need.
The root owns the shared size, variant, placement, and backdrop and hands them to the parts through context, so you set them once on <Modal> and the header, icon, body, and footer follow. ModalContent already renders the portal, the backdrop, and (by default) a corner close button — you never add ModalPortal or ModalBackdrop yourself.
Reach for it for a blocking, focused interaction: a confirmation, a create/edit form, a destructive prompt, or a full-screen task. For content anchored to a trigger that shouldn't block the page, use a Popover; for a passive hint, use a Tooltip.
Modal is a Create UI Pro component. With a Pro seat, npx @create-ui/cli add modal installs it. The previews below are marked with a Pro badge.
Installation
Anatomy
ModalContent wraps the portal, backdrop, and close button. Put the header and scrollable body inside ModalMain, then place ModalFooter as its sibling inside ModalContent.
ModalMain groups the header and body into the white card; ModalFooter sits on the tray below it. Render a ModalTitle (and ideally a ModalDescription) so the dialog is labelled for assistive tech.
Usage
The root forwards Radix Dialog props, so control it with open / onOpenChange or leave it uncontrolled with defaultOpen:
Examples
Every example is a Pro preview (marked with a Pro badge). Each renders a trigger button; open it to see the modal.
Sizes
size sets the card width and cascades to the header, icon, body, and footer. sm and md are centered cards; cover fills the viewport (minus a gutter) for full-screen tasks. Long bodies scroll inside ModalBody while the header and footer stay pinned.
Placement
placement sets where the card sits in the viewport — top, center, bottom, or auto (centered). It positions the card; it is not a margin.
Backdrop
backdrop sets the overlay treatment: opaque dims the page, blur dims and blurs it, and transparent leaves it visible. Match the weight to the interaction.
Header alignment
ModalHeader takes an align: horizontal places the icon beside the title and description, vertical-left stacks them left-aligned, and vertical-center stacks and centers them (common for success and confirmation dialogs).
Icon
ModalIcon renders a decorative glyph whose color follows the modal variant. Tune its look with appearance (solid, soft, neutral, outline), type (stylish adds a gradient fill and ring, plain is flat), and shape (rounded or circle). Put the icon as its single child — the component sizes it.
Footer
ModalFooter takes align — end clusters the actions to the right, between pushes a secondary control (a checkbox, a link) to the left — and orientation — horizontal or vertical for stacked full-width buttons.
Close button
ModalContent renders a corner close button by default. Set showCloseButton={false} to hide it; the modal still closes with Escape, an outside click, or any control you wrap in ModalClose.
Body content
ModalBody is a free slot — compose any of your own components inside it and it scrolls independently while the header and footer stay pinned. The same modal shell can carry a form (Field + Input), a settings panel (Switch toggles), a list (Avatar + Checkbox rows), a media banner (AspectRatio + Badge), or anything else you build — open each below. Radix moves focus to the content container rather than the first field, so add autoFocus to an input yourself if you want it focused on open.
Accessibility
Modal is built on Radix Dialog, which provides the focus trap, scroll lock, Escape-to-close, and aria wiring.
ARIA notes:
- Render a
ModalTitle(and ideally aModalDescription) insideModalHeaderContentso the dialog is labelled — Radix logs a warning if a title is missing. If you omit the visible title, supply your ownaria-labelonModalContent. - Focus is intentionally moved to the content container on open (
onOpenAutoFocusis overridden), so the first field is not focused automatically. AddautoFocusto an input when a form should focus it. - The corner close button (and any
ModalClose) is a real<button>;CloseButtonprovides its ownaria-label. - Opening locks page scroll and traps focus; closing restores focus to the trigger.
Styling
Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()):
Data slots and attributes: the components set these for CSS targeting:
data-slot="modal-trigger"on the trigger.data-slot="modal-backdrop"on the overlay, withdata-backdrop.data-slot="modal-container"on the positioning layer, withdata-sizeanddata-placement.data-slot="modal-content"on the card, withdata-size,data-placement, anddata-variant.data-slot="modal-header"on the header, withdata-alignanddata-size;data-slot="modal-header-content"on the title/description column.data-slot="modal-icon"on the icon, withdata-variant,data-size,data-appearance,data-type, anddata-shape.data-slot="modal-title"anddata-slot="modal-description"on the text.data-slot="modal-main"on the card body wrapper anddata-slot="modal-body"on the scroll region.data-slot="modal-footer"on the footer, withdata-orientation,data-align, anddata-size.data-slot="modal-close"onModalClose.
Target a specific state in CSS:
Related Components
- Popover: use this for non-blocking content anchored to a trigger, not a page-blocking overlay.
- Tooltip: use this for a passive hint on hover or focus.
- Close Button: the dismiss control
ModalContentrenders andModalClosewraps. - Scroll Area: powers the scrollable
ModalBody. - Button: the trigger and footer actions.
API Reference
Modal
Root that owns the shared context and forwards to Radix Dialog.Root. Extends React.ComponentProps<typeof Dialog.Root>, so open, defaultOpen, onOpenChange, and modal are accepted.
Props
Variants
ModalTrigger
The element that opens the modal. Wraps Radix Dialog.Trigger; use asChild to render your own Button. Renders <button data-slot="modal-trigger">.
ModalContent
The card. Renders the portal, backdrop, and (by default) the corner close button, then your children. Extends React.ComponentProps<typeof Dialog.Content>.
Props
ModalClose
Wraps Radix Dialog.Close; use asChild to turn any element (a footer Button) into a dismiss control.
ModalHeader
The header row inside ModalMain. Provides align/size context to the title and description. Extends React.ComponentProps<"div">.
Props
ModalHeaderContent
Column that stacks the title and description. Extends React.ComponentProps<"div">.
ModalIcon
Decorative glyph whose color follows the modal variant. Takes the icon as its single child and sizes it (size-7 at md, size-6 at sm). Extends React.ComponentProps<"span">.
Props
ModalTitle / ModalDescription
Wrap Radix Dialog.Title and Dialog.Description. Render them inside ModalHeaderContent; the title labels the dialog for assistive tech.
ModalMain
Groups the header and body into the white card. Extends React.ComponentProps<"div">.
ModalBody
The scrollable content region. Wraps children in a ScrollArea capped at max-h-100 (uncapped when size="cover") — drop plain content inside, no extra scroll container needed. Extends React.ComponentProps<"div">.
ModalFooter
The action tray below the card. Extends React.ComponentProps<"div"> (the size comes from context).
Props
ModalBackdrop / ModalPortal
ModalContent renders both automatically — you rarely use them directly. ModalBackdrop (the overlay) takes a backdrop prop that overrides the root's; ModalPortal wraps Radix Dialog.Portal.