# Dialog URL: https://ark-ui.com/docs/components/dialog Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/components/dialog.mdx A modal window that appears on top of the main content. --- ## Anatomy ```tsx ``` ## Examples **Example: basic** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; export component Basic() { {'Open Dialog'} {'✕'} {'Welcome Back'} {'Sign in to your account to continue.'} } ``` ### Controlled Manage the dialog state using the `open` and `onOpenChange` props. **Example: controlled** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import { track } from 'ripple'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; export component Controlled() { let open = track(false); { @open = e.open; }} > {'Open Dialog'} {'✕'} {'Session Settings'} {'Manage your session preferences and security options.'} } ``` ### Root Provider An alternative way to control the dialog is to use the `RootProvider` component and the `useDialog` hook. This way you can access the state and methods from outside the component. **Example: root-provider** ```ripple import { Dialog, useDialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; export component RootProvider() { const dialog = useDialog();
{'✕'} {'Controlled Externally'} {'This dialog is controlled via the useDialog hook.'}
} ``` ### Alert Dialog For critical confirmations or destructive actions, use `role="alertdialog"`. Alert dialogs differ from regular dialogs in important ways: - **Automatic focus**: The close/cancel button receives focus when opened, prioritizing the safest action - **Requires explicit dismissal**: Cannot be closed by clicking outside, only via button clicks or Escape key **Example: alert-dialog** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; export component AlertDialog() { {'Delete Account'} {'Are you absolutely sure?'} {'This action cannot be undone. This will permanently delete your account and remove your data from our servers.'}
{'Cancel'}
} ``` ### Lazy Mount Use `lazyMount` to render dialog content only when first opened. Combine with `unmountOnExit` to unmount when closed, freeing up resources. **Example: lazy-mount** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; export component LazyMount() { {'Open Dialog'} {'✕'} {'Lazy Loaded'} {'This dialog content is only mounted when opened and unmounts on close.'} } ``` ### Initial Focus Use `initialFocusEl` to control which element receives focus when the dialog opens. **Example: initial-focus** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import field from 'styles/field.module.css'; import styles from 'styles/dialog.module.css'; export component InitialFocus() { let inputEl: HTMLInputElement; inputEl}> {'Open Dialog'} {'✕'} {'Edit Profile'} {'The first input will be focused when the dialog opens.'}
{ inputEl = el; }} class={field.Input} placeholder="Enter your name..." />
} ``` ### Final Focus Use `finalFocusEl` to control which element receives focus when the dialog closes. Defaults to the trigger element. **Example: final-focus** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; export component FinalFocus() { let finalRef: HTMLButtonElement;
finalRef}> {'Open Dialog'} {'✕'} {'Focus Redirect'} {'When this dialog closes, focus will return to the button above instead of the trigger.'}
} ``` ### Non-Modal Use `modal={false}` to allow interaction with elements outside the dialog. Disables focus trapping and scroll prevention. **Example: non-modal** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; export component NonModal() { {'Open Non-Modal Dialog'} {'✕'} {'Non-Modal Dialog'} {'This dialog allows interaction with elements outside. You can click buttons, select text, and interact with the page behind it.'} } ``` ### Inside Scroll Make the content area scrollable while keeping header and footer fixed using `maxHeight` and `overflow: auto`. **Example: inside-scroll** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; const CONTENT_SECTIONS = [ { title: '1. Acceptance of Terms', body: 'By accessing and using this service, you accept and agree to be bound by the terms and provisions of this agreement.', }, { title: '2. Use License', body: 'Permission is granted to temporarily use this service for personal, non-commercial purposes only. This is the grant of a license, not a transfer of title.', }, { title: '3. User Responsibilities', body: 'You are responsible for maintaining the confidentiality of your account and password. You agree to accept responsibility for all activities that occur under your account.', }, { title: '4. Privacy Policy', body: 'Your use of this service is also governed by our Privacy Policy. Please review our Privacy Policy, which also governs the site and informs users of our data collection practices.', }, { title: '5. Limitations', body: 'In no event shall we be liable for any damages arising out of the use or inability to use the materials on this service.', }, { title: '6. Revisions', body: 'We may revise these terms of service at any time without notice. By using this service you are agreeing to be bound by the then current version of these terms.', }, { title: '7. Governing Law', body: 'These terms and conditions are governed by and construed in accordance with applicable laws and you irrevocably submit to the exclusive jurisdiction of the courts.', }, ]; export component InsideScroll() { {'Open Dialog'} {'✕'} {'Terms of Service'} {'Please review our terms before continuing.'}
for (const item of CONTENT_SECTIONS; key item.title) {

{item.title}

{item.body}

}
{'Decline'} {'Accept'}
} ``` ### Outside Scroll Make the positioner scrollable so the dialog can extend beyond the viewport. **Example: outside-scroll** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; const CONTENT_SECTIONS = [ { title: '1. Information We Collect', body: 'We collect information you provide directly, such as when you create an account, make a purchase, or contact us for support. This may include your name, email address, and payment information.', }, { title: '2. How We Use Your Information', body: 'We use the information we collect to provide and improve our services, process transactions, send communications, and personalize your experience.', }, { title: '3. Information Sharing', body: 'We do not sell your personal information. We may share information with service providers who assist in our operations, or when required by law.', }, { title: '4. Data Security', body: 'We implement appropriate technical and organizational measures to protect your personal information against unauthorized access, alteration, or destruction.', }, { title: '5. Your Rights', body: 'You have the right to access, correct, or delete your personal information. You may also opt out of marketing communications at any time.', }, { title: '6. Cookies and Tracking', body: 'We use cookies and similar technologies to enhance your experience, analyze usage patterns, and deliver targeted content. You can manage cookie preferences in your browser settings.', }, { title: '7. Third-Party Services', body: 'Our service may contain links to third-party websites. We are not responsible for the privacy practices of these external sites.', }, { title: '8. Children Privacy', body: 'Our services are not directed to children under 13. We do not knowingly collect personal information from children without parental consent.', }, { title: '9. International Transfers', body: 'Your information may be transferred to and processed in countries other than your own. We ensure appropriate safeguards are in place for such transfers.', }, { title: '10. Changes to This Policy', body: 'We may update this privacy policy from time to time. We will notify you of significant changes by posting a notice on our website or sending you an email.', }, { title: '11. Data Retention', body: 'We retain your personal information for as long as necessary to fulfill the purposes outlined in this policy, unless a longer retention period is required by law.', }, { title: '12. Contact Us', body: 'If you have questions about this privacy policy or our data practices, please contact our privacy team through the support channels provided on our website.', }, ]; export component OutsideScroll() { let contentRef: HTMLDivElement | null = null; contentRef}> {'Open Dialog'} { contentRef = el; }} class={styles.Content} style={{ margin: '4rem auto', maxHeight: 'none' }} > {'✕'} {'Privacy Policy'} {'This layout allows the dialog to extend beyond the viewport while keeping the outer container scrollable.'}
for (const item of CONTENT_SECTIONS; key item.title) {

{item.title}

{item.body}

}
} ``` ### Context Access the dialog's state and methods with `Dialog.Context` or the `useDialogContext` hook. **Example: context** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; export component Context() { {'Open Dialog'} {'✕'} {'Status'} component children({ context }) { {'Dialog is '} {@context.open ? 'open' : 'closed'} } } ``` ### Open from Menu Open a dialog imperatively from a menu item using the `onClick` handler. **Example: open-from-menu** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Menu } from 'ark-ripple/menu'; import { Portal } from 'ark-ripple/portal'; import { ChevronDown, X } from 'lucide-ripple'; import { track } from 'ripple'; import dialog from 'styles/dialog.module.css'; import menu from 'styles/menu.module.css'; export component OpenFromMenu() { let open = track(false); {'Actions'} {'Edit'} {'Duplicate'} { @open = true; }} > {'Delete...'} { @open = e.open; }} role="alertdialog" > {'Confirm Delete'} {'Are you sure you want to delete this item? This action cannot be undone.'} } ``` ### Nested Nest dialogs within one another. The parent receives `data-has-nested` and `--nested-layer-count` CSS variable for styling effects like zoom-out: ```css [data-part='content'][data-has-nested] { transform: scale(calc(1 - var(--nested-layer-count) * 0.05)); } ``` **Example: nested** ```ripple import { Dialog, useDialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import button from 'styles/button.module.css'; import styles from 'styles/dialog.module.css'; export component Nested() { const parentDialog = useDialog(); const childDialog = useDialog();
{'✕'} {'Parent Dialog'} {'This is the parent dialog.'}
{'✕'} {'Nested Dialog'} {'This dialog is nested within the parent.'}
} ``` ### Confirmation Intercept close attempts to show confirmation prompts, preventing data loss from unsaved changes. **Example: confirmation** ```ripple import { Dialog, useDialog } from 'ark-ripple/dialog'; import { Portal } from 'ark-ripple/portal'; import { track } from 'ripple'; import button from 'styles/button.module.css'; import field from 'styles/field.module.css'; import styles from 'styles/dialog.module.css'; export component Confirmation() { let formContent = track(''); let isParentDialogOpen = track(false); const confirmDialog = useDialog(); let parentDialogProps = track( () => ({ open: isParentDialogOpen, onOpenChange: (details) => { if (!details.open && @formContent.trim()) { @confirmDialog.setOpen(true); } else { @isParentDialogOpen = details.open; } }, }), ); const parentDialog = useDialog(parentDialogProps); const handleConfirmClose = () => { @formContent = ''; const childSetter = @confirmDialog.setOpen; const parentSetter = @parentDialog.setOpen; childSetter(false); parentSetter(false); };
{'✕'} {'Edit Content'} {'Make changes to your content. You\'ll be asked to confirm before closing if there are unsaved changes.'}