How to use
Modal is a focus-trapping, portal-rendered dialog for content that needs the user's full attention before they continue — confirmations, forms that block the underlying page, detail views opened from a list. It's fully controlled: you own the open/closed state, Modal just renders it correctly and safely.
import { Modal, Button } from "oks-ui";isOpen and onClose are both required — Modal never manages its own visibility. onClose fires from every dismissal path (backdrop click, Escape, the header's close button) so there's exactly one place to update your state, regardless of how the user closed it.
function DeleteConfirm() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button color="danger" onPress={() => setIsOpen(true)}>Delete item</Button>
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Delete item"
actions={
<>
<Button variant="soft" onPress={() => setIsOpen(false)}>Cancel</Button>
<Button color="danger" onPress={handleDelete}>Delete</Button>
</>
}
>
This can't be undone. Are you sure?
</Modal>
</>
);
}title renders the header (built on PageTitle internally, with a close button next to it unless dismissible={false}), children is the body, and actions is the footer — typically Cancel/Confirm-style buttons. All three are optional independently, so a Modal can be body-only, or header+body with no footer.
Opening a second Modal (or a Drawer, since they share the same overlay stack) while one is already open works correctly out of the box — z-index and backdrop layering are computed automatically, and Escape only ever closes the topmost overlay, not every open one at once.
// Nested confirm-inside-a-modal -- stacks correctly without manual z-index math
<Modal isOpen={editOpen} onClose={() => setEditOpen(false)} title="Edit profile">
<ProfileForm />
<Modal
isOpen={confirmDiscard}
onClose={() => setConfirmDiscard(false)}
title="Discard changes?"
role="alertdialog"
>
Unsaved changes will be lost.
</Modal>
</Modal>size accepts the usual token scale plus "full" for an edge-to-edge modal, or a raw string/number for an exact width. animationType controls how it enters/exits (fade, zoom, or a directional slide) with animationDuration/easing to fine-tune the feel; blur and backgroundOpacity control the backdrop underneath it.
<Modal isOpen={isOpen} onClose={onClose} size="lg" animationType="slideUp" title="Details">
{content}
</Modal>For a modal whose sole purpose is a decision the user must make (not just informational content), set role="alertdialog" instead of the default "dialog" — screen readers announce it more assertively, matching the native alertdialog semantics.
All available props for Modal. See the full component page for an interactive playground.
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | — | Header title. |
children | ReactNode | — | Body content. |
actions | ReactNode | — | Footer action buttons. |
title
ReactNode
Default: —
Header title.
children
ReactNode
Default: —
Body content.
actions
ReactNode
Default: —
Footer action buttons.
| Prop | Type | Default | Description |
|---|---|---|---|
size | "xs" | "sm" | "md" | "lg" | "xl" | "full" | string | number | md | Modal width. |
blur | boolean | "sm" | "md" | "lg" | number | — | Backdrop blur amount. |
backgroundOpacity | number | — | Backdrop opacity (0-100). |
size
"xs" | "sm" | "md" | "lg" | "xl" | "full" | string | number
Default: md
Modal width.
blur
boolean | "sm" | "md" | "lg" | number
Default: —
Backdrop blur amount.
backgroundOpacity
number
Default: —
Backdrop opacity (0-100).
| Prop | Type | Default | Description |
|---|---|---|---|
portal | boolean | true | Renders through a React portal. |
container | Element | DocumentFragment | null | — | Custom portal mount target. |
zIndex | number | — | Explicit stacking z-index; auto-computed when unset. |
portal
boolean
Default: true
Renders through a React portal.
container
Element | DocumentFragment | null
Default: —
Custom portal mount target.
zIndex
number
Default: —
Explicit stacking z-index; auto-computed when unset.
| Prop | Type | Default | Description |
|---|---|---|---|
isOpen | boolean | — | Controls whether the modal is open (required). |
role | "dialog" | "alertdialog" | dialog | ARIA role. |
dismissible | boolean | true | Shows a close button in the header. |
closeLabel | string | "Close modal" | Accessible label for the close button. |
closeOnOutsideClick | boolean | true | Closes on backdrop click. |
closeOnEscape | boolean | true | Closes on Escape. |
initialFocusRef | RefObject<HTMLElement | null> | — | Element to focus when the modal opens. |
isOpen
boolean
Default: —
Controls whether the modal is open (required).
role
"dialog" | "alertdialog"
Default: dialog
ARIA role.
dismissible
boolean
Default: true
Shows a close button in the header.
closeLabel
string
Default: "Close modal"
Accessible label for the close button.
closeOnOutsideClick
boolean
Default: true
Closes on backdrop click.
closeOnEscape
boolean
Default: true
Closes on Escape.
initialFocusRef
RefObject<HTMLElement | null>
Default: —
Element to focus when the modal opens.
| Prop | Type | Default | Description |
|---|---|---|---|
animationDuration | number | — | Enter/exit animation duration in seconds. |
animationType | "fade" | "zoom" | "slideUp" | "slideDown" | "slideLeft" | "slideRight" | — | Backdrop entrance/exit animation. |
easing | "ease" | "easeIn" | "easeOut" | "easeInOut" | — | Animation easing curve. |
animationDuration
number
Default: —
Enter/exit animation duration in seconds.
animationType
"fade" | "zoom" | "slideUp" | "slideDown" | "slideLeft" | "slideRight"
Default: —
Backdrop entrance/exit animation.
easing
"ease" | "easeIn" | "easeOut" | "easeInOut"
Default: —
Animation easing curve.
| Prop | Type | Default | Description |
|---|---|---|---|
onClose | () => void | — | Called to close the modal (required). |
onClose
() => void
Default: —
Called to close the modal (required).
| Prop | Type | Default | Description |
|---|---|---|---|
backdrop | Partial<BackdropProps> | — | Extra props forwarded to the underlying Backdrop. |
headers | Partial<PageTitleProps> | — | Extra props forwarded to the header's PageTitle. |
divider | Partial<DividerProps> | — | Extra props for the header/body divider. |
classNames | Partial<Record<ModalSlot, string>> | — | Per-slot class overrides (base/header/headerTitle/body/footer/closeButton). |
className | string | — | Class applied to the root modal element. |
style | CSSProperties | — | Inline styles for the root element. |
backdrop
Partial<BackdropProps>
Default: —
Extra props forwarded to the underlying Backdrop.
headers
Partial<PageTitleProps>
Default: —
Extra props forwarded to the header's PageTitle.
divider
Partial<DividerProps>
Default: —
Extra props for the header/body divider.
classNames
Partial<Record<ModalSlot, string>>
Default: —
Per-slot class overrides (base/header/headerTitle/body/footer/closeButton).
className
string
Default: —
Class applied to the root modal element.
style
CSSProperties
Default: —
Inline styles for the root element.