Skip to content
FelicityUI

Dialog

A modal confirmation surface. Native presentation on every platform; shared intent, tokens, and naming.

Edit profile

Make changes to your profile here. Click save when you're done.

Installation

terminal
npx felicityui add dialog --platform react

Usage

example.tsx
import { Button } from "@/components/ui/button"
import { Dialog, DialogFooter } from "@/components/ui/dialog"

<Dialog
  open={open}
  onOpenChange={setOpen}
  title="Are you absolutely sure?"
  description="This action cannot be undone. This will permanently delete the draft."
>
  <DialogFooter>
    <Button variant="ghost" onClick={() => setOpen(false)}>
      Cancel
    </Button>
    <Button onClick={() => setOpen(false)}>Continue</Button>
  </DialogFooter>
</Dialog>

Composition

Use the following composition to build a Dialog.

Dialog
├── title
├── description
└── children
    ├── DialogBody
    │   ├── Name
    │   └── Username
    └── DialogFooter
        ├── Cancel
        └── Confirm

Use Dialog to confirm or dismiss a short decision. Do not use it for navigation, forms that scroll, or a persistent side panel — those belong to Drawer / Sheet.

When to use

  • Confirm a destructive action (delete, discard, sign out).
  • Acknowledge a blocking choice with one or two actions.
  • A couple of short fields (name, username) in DialogBody, with actions in DialogFooter.
  • On the web only: a short scrolling list inside DialogBody, with actions pinned in DialogFooter.

When not to use

  • Long settings or a scrolling profile editor. Use Drawer.
  • Non-modal help or inline validation.
  • Navigation. Use the platform's navigation primitives.

Confirm

Two actions plus a couple of short fields. Put fields in DialogBody on the web. SwiftUI uses TextField in the alert; Compose puts fields in the AlertDialog text slot.

Edit profile

Make changes to your profile here. Click save when you're done.

Destructive

Irreversible confirm. Delete stays disabled until the typed phrase matches. Native destructive role on the confirm action.

Delete file?

This cannot be undone. Type DELETE to confirm.

Custom dismiss

Replace Cancel with your own dismiss action. Dialog does not ship a close icon — actions are children.

Unsaved changes

Replace Cancel with your own dismiss action.

Share link

Single Done action with a copyable MCP URL in the field. Omit Cancel. Native share uses ShareLink / the system share sheet.

Share link

Copy the MCP endpoint. Anyone with it can connect.

Scrollable content

On the web, DialogBody scrolls while the title and DialogFooter stay put. SwiftUI and Compose keep this as the system alert message — longer layouts belong on Drawer.

Delete 24 items?

These unpublished drafts will be removed from every device. Title and actions stay put while the list scrolls.

24 files in Drafts. This cannot be undone. Linked comments and share URLs will stop resolving.

  • Q3 roadmap.md
    Unpublished · 28 KB · Edited 20 minutes ago
  • Brand notes.md
    Unpublished · 12 KB · Edited 2 hours ago
  • Onboarding copy.md
    Unpublished · 41 KB · Edited yesterday
  • Pricing experiments.md
    Unpublished · 9 KB · Edited yesterday
  • Release checklist.md
    Unpublished · 6 KB · Edited 2 days ago
  • Support macros.md
    Unpublished · 18 KB · Edited 2 days ago
  • Interview notes.md
    Unpublished · 33 KB · Edited 3 days ago
  • Accessibility audit.md
    Unpublished · 22 KB · Edited 4 days ago
  • Token inventory.md
    Unpublished · 15 KB · Edited 5 days ago
  • Dialog variants.md
    Unpublished · 11 KB · Edited last week
  • Button states.md
    Unpublished · 8 KB · Edited last week
  • Drawer research.md
    Unpublished · 19 KB · Edited last week
  • iOS alert notes.md
    Unpublished · 7 KB · Edited last week
  • Compose scrim.md
    Unpublished · 5 KB · Edited 2 weeks ago
  • VoiceOver checklist.md
    Unpublished · 13 KB · Edited 2 weeks ago
  • TalkBack labels.md
    Unpublished · 10 KB · Edited 2 weeks ago
  • RTL review.md
    Unpublished · 16 KB · Edited 3 weeks ago
  • Focus rings.md
    Unpublished · 4 KB · Edited 3 weeks ago
  • Overlay opacity.md
    Unpublished · 3 KB · Edited last month
  • Scroll lock.md
    Unpublished · 6 KB · Edited last month
  • Share MCP.md
    Unpublished · 9 KB · Edited last month
  • Type to confirm.md
    Unpublished · 8 KB · Edited last month
  • Profile fields.md
    Unpublished · 14 KB · Edited last month
  • Archive index.md
    Unpublished · 52 KB · Edited last month

RTL

Pass dir="rtl" on the React dialog. SwiftUI and Compose follow the system layout direction.

حذف الملف؟

لا يمكن التراجع عن هذا الإجراء.

API Reference

Dialog

PropTypeDefaultDescription
open / isPresentedboolean / Binding<Bool>Whether the dialog is shown. SwiftUI uses isPresented.
onOpenChange / onDismiss(open: boolean) => void / () -> UnitCalled when the user dismisses. React uses onOpenChange; Compose uses onDismiss.
titlestringAccessible name. Required.
description / message / textstringOptional supporting copy. Named description (React), message (SwiftUI), text (Compose).
childrenReactNodeOptional DialogBody plus DialogFooter actions. Compose Button here; Dialog does not import it.
confirmTitle / confirmLabelstringOKConfirm action label on SwiftUI and Compose.
cancelTitle / cancelLabelstring | nullCancelDismiss action label on SwiftUI and Compose. Pass nil / null to omit the dismiss action.
destructivebooleanfalseMarks the confirm action as dangerous.
onConfirm() -> Void / () -> UnitConfirm handler on SwiftUI and Compose.
classNamestringAdditional classes on the dialog element.
dir"ltr" | "rtl"Text direction on the React dialog. Native platforms follow the system layout direction.

Accessibility

WCAG 2.2 AA

  • Must be exposed as a dialog (or the platform alert equivalent), not a custom overlay div.
  • Title is the accessible name.
  • Description, when present, is the accessible description.
  • Focus moves into the dialog when it opens and returns to the invoker when it closes.
  • Escape or the platform dismiss gesture closes a non-required dialog.
  • Confirm and cancel are real buttons with visible names.

Platform notes

React

Renders a native HTML <dialog> with showModal(). Focus trap, Escape, and backdrop dismiss come from the element, not a portal overlay.

  • Client Component: showModal() must run in the browser.
  • Compose actions with Button in children. Dialog does not import Button.
  • Prefer type="button" on actions inside the dialog.
  • DialogBody holds a short list or a couple of fields; DialogFooter stays put. Longer forms belong on Drawer.
  • Pass dir="rtl" on the dialog for RTL. There is no close icon — dismiss is a composed action.
  • The ::backdrop is a dark translucent dim. Native modal dialogs cover the whole viewport, including chrome; the page stays visible through the overlay.
  • Document scroll is locked while the dialog is open. DialogBody may still scroll.

SwiftUI

SwiftUI .alert modifier. Not a web-style card. Destructive confirm uses Role.destructive.

  • VoiceOver announces the system alert title and message.
  • Dynamic Type is the system alert type size.
  • Omit cancelTitle (pass nil) for a single confirm action.
  • Short prompts can add TextField in the alert actions. Richer layouts belong on Sheet / Drawer.

Compose

Material3 AlertDialog with FelicityUI colors. Interaction (scrim, back, talkback) stays platform-native.

  • TalkBack announces the title.
  • Back and scrim call onDismiss.
  • Omit cancelLabel (pass null) for a single confirm action.
  • A couple of OutlinedTextField rows can live in the text slot. Do not restyle this into a web card; use a sheet for longer content.