On this page
Modal
A dialog window that appears above the main content with an overlay backdrop.
import { Modal } from "@ninna-ui/overlays";pnpm add @ninna-ui/overlays@latestIncludes
@ninna-ui/core. CSS setup required.View source 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.
Live preview/ Default
Sizes
Modal supports xs, sm, md, lg, xl, and full sizes.
Live preview/ Default
Centered
Toggle vertical centering of the modal dialog.
Live preview/ Default
Controlled
Control the modal open state via React state.
Live preview/ Default
Modal is closed
Prevent Close
Disable overlay click and Escape key to create persistent dialogs.
Live preview/ Default
Long Content
Modal body scrolls automatically when content overflows.
Live preview/ Default
With Description
Add an accessible description to the modal for better screen reader support.
Live preview/ Default
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