Modal
Compound modal dialog. Renders into a portal, closes on overlay click or Escape key. Supports controlled and uncontrolled state.
Features
Portal rendering outside the document tree.
Closes on Escape key and overlay click.
Can be controlled or uncontrolled.
Renders with role=“dialog” and aria-modal.
data-state reflects open/closed state.
Example
Anatomy
import { useState } from 'react'
import { Modal } from '@wire-ui/react'
const [open, setOpen] = useState(false)
<button onClick={() => setOpen(true)}>Delete Account</button>
<Modal.Root open={open} onOpenChange={setOpen}>
<Modal.Portal>
<Modal.Overlay>
<Modal.Content>
<h2>Delete Account</h2>
<p>
Are you sure you want to delete your account? All of your data will be permanently removed. This action cannot be undone.
</p>
<div>
<Modal.Close>Cancel</Modal.Close>
<button onClick={() => setOpen(false)}>Delete</button>
</div>
</Modal.Content>
</Modal.Overlay>
</Modal.Portal>
</Modal.Root>Styling
Modal sets data-state on both Overlay and Content. Use it to control visibility and add enter/exit animations.
<Modal.Overlay className="
data-[state=open]:opacity-100
data-[state=closed]:opacity-0
transition-opacity
" />
<Modal.Content className="
data-[state=open]:scale-100
data-[state=closed]:scale-95
transition-transform
" />Using data attributes
Both Overlay and Content receive data-state with values "open" or "closed".
/* Fade in/out overlay */
[data-state="open"] { opacity: 1; }
[data-state="closed"] { opacity: 0; pointer-events: none; }
/* Scale content */
[data-state="open"] { transform: scale(1); }
[data-state="closed"] { transform: scale(0.95); }Root props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state |
defaultOpen | boolean | false | Initial state for uncontrolled use |
onOpenChange | (open: boolean) => void | — | Called when open state changes |
Data attributes
data-state is set on both Modal.Overlay and Modal.Content:
| Value | When |
|---|---|
"open" | Modal is open |
"closed" | Modal is closed |
Behaviour
- Portal — content is rendered outside the document tree via
createPortal - Escape key — closes the modal
- Overlay click — closes the modal
Modal.Content— renders withrole="dialog"andaria-modal="true"Modal.Close— any button inside that callsonOpenChange(false)on click
Animation
Use data-state with CSS transitions:
[data-state="open"] { animation: fadeIn 150ms ease; }
[data-state="closed"] { animation: fadeOut 150ms ease; }Keyboard Interactions
| Key | Description |
|---|---|
| Escape | Closes the modal. |
| Tab | Moves focus to the next focusable element inside the modal. |
| Shift+Tab | Moves focus to the previous focusable element inside the modal. |
Last updated on