Modal
A dialog window that appears above the main content with an overlay backdrop.
import { Modal } from "@ninna-ui/overlays"pnpm add @ninna-ui/overlaysRequires @ninna-ui/core (auto-installed) + CSS setup.
Usage
Import and use the Modal component in your application.
import { Modal } from "@ninna-ui/overlays";import { Button , Code } from "@ninna-ui/primitives";
export default function Example() { return ( <Modal> <Modal.Trigger asChild> <Button>Open Modal</Button> </Modal.Trigger> <Modal.Content> <Modal.Header>Title</Modal.Header> <Modal.Body>Content here</Modal.Body> <Modal.Footer> <Modal.Close asChild> <Button variant="ghost">Cancel</Button> </Modal.Close> <Button>Confirm</Button> </Modal.Footer> </Modal.Content> </Modal> );}Basic
A basic modal dialog with header, body, and footer.
Sizes
Modal supports xs, sm, md, lg, xl, and full sizes.
Centered
Toggle vertical centering of the modal dialog.
Controlled
Control the modal open state via React state.
Modal is closed
Prevent Close
Disable overlay click and Escape key to create persistent dialogs.
Long Content
Modal body scrolls automatically when content overflows.
With Description
Add an accessible description to the modal for better screen reader support.
API Reference
Complete list of props for the Modal component and its sub-components.
Modal (Root)
| Prop | Type | Default | Description |
|---|---|---|---|
children* | ReactNode | - | Modal content (Trigger, Content, etc.) |
open | boolean | - | Controlled open state |
defaultOpen | boolean | false | Default open state (uncontrolled) |
onOpenChange | (open: boolean) => void | - | Callback when open state changes |
modal | boolean | true | Whether the modal blocks interaction with the rest of the page |
Modal.Content
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full' | 'md' | Size of the modal content panel |
centered | boolean | true | Whether the modal is vertically centered |
description | string | - | Accessible description for the modal. Rendered as sr-only when provided. |
closeOnOverlayClick | boolean | true | Whether clicking the overlay closes the modal |
closeOnEscape | boolean | true | Whether pressing Escape closes the modal |
onEscapeKeyDown | (event: KeyboardEvent) => void | - | Callback when Escape key is pressed |
onPointerDownOutside | (event: Event) => void | - | Callback when pointer clicks outside content |
onInteractOutside | (event: Event) => void | - | Callback when focus moves outside content |
className | string | - | Additional CSS classes |
Modal.Trigger
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Render as child element instead of wrapping button |
Modal.Close
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Render as child element instead of default close button |
Accessibility
- Uses
role="dialog"witharia-modal="true" - Focus is trapped inside the modal when open
- Focus returns to the trigger element when the modal is closed
- Escape key closes the modal by default (configurable)
- Overlay click closes the modal by default (configurable)
- Header content is automatically used as the dialog label