Dialog

Modal dialog with a backdrop and footer actions.

A dialog open over a dimmed screen. The panel reads Delete project? above the line This action cannot be undone. The project and all of its data will be permanently removed, with a plain Cancel button and a red Delete button in the footer.
A destructive confirmation, with the backdrop dimming the screen behind it.

A modal dialog with a backdrop and footer actions.

It mounts through a portal and unmounts after its exit animation.

Use it when the reader has to deal with something before carrying on. When the surrounding context needs to stay visible, use Popover; on a phone, a BottomSheet is usually the better shape for anything with more than two lines in it.

Installation

Dialog ships with the library — no separate install.

import { Dialog, Button, Input } from 'panelui-native';
import { View } from 'react-native';

Or copy the source into your project, to own and edit it:

npx panelui-cli@latest add dialog

Usage

<Dialog>
  <Dialog.Trigger>
    <Button variant="outline">Delete</Button>
  </Dialog.Trigger>
  <Dialog.Content>
    <Dialog.Title>Delete project?</Dialog.Title>
    <Dialog.Description>
      This action cannot be undone.
    </Dialog.Description>
    <Dialog.Footer>
      <Dialog.Close>
        <Button size="sm" variant="ghost">Cancel</Button>
      </Dialog.Close>
      <Dialog.Close>
        <Button size="sm" variant="destructive">Delete</Button>
      </Dialog.Close>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog>

Composition

<Dialog>
  <Dialog.Trigger>…</Dialog.Trigger>
  <Dialog.Content>
    <Dialog.Title>…</Dialog.Title>
    <Dialog.Description>…</Dialog.Description>
    <Dialog.Footer>
      <Dialog.Close>…</Dialog.Close>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog>
  • Dialog.Trigger — Clones its child and opens the dialog on press.
  • Dialog.Content — The dialog surface, rendered in a portal.
  • Dialog.Title — Required for accessibility.
  • Dialog.Description — Supporting text.
  • Dialog.Footer — Row of actions, aligned to the trailing edge. variant="panel" draws it as a band instead — a rule across the top, a step darker, and the dialog's own bottom corners, bleeding out through the dialog's padding to reach its edges.
  • Dialog.Close — Clones its child and closes the dialog on press.

Examples

Uncontrolled, with a trigger

<Dialog>
  <Dialog.Trigger>
    <Button variant="outline">Rename</Button>
  </Dialog.Trigger>
  <Dialog.Content>
    <Dialog.Title>Rename project</Dialog.Title>
    <Dialog.Description>This is visible to everyone on the team.</Dialog.Description>
    <Input className="mt-4" defaultValue={project.name} />
    <Dialog.Footer>
      <Dialog.Close>
        <Button variant="ghost">Cancel</Button>
      </Dialog.Close>
      <Button onPress={save}>Save</Button>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog>

Controlled confirmation

dismissible={false} forces a deliberate choice — the backdrop no longer closes it.

const [open, setOpen] = useState(false);

<Dialog open={open} onOpenChange={setOpen}>
  <Dialog.Content dismissible={false}>
    <Dialog.Title>Delete 12 files?</Dialog.Title>
    <Dialog.Description>This cannot be undone.</Dialog.Description>
    <Dialog.Footer>
      <Button variant="ghost" onPress={() => setOpen(false)}>Cancel</Button>
      <Button variant="destructive" onPress={confirm}>Delete</Button>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog>

A blurred background

Pass blur to frost the screen behind the dialog instead of dimming it. It uses expo-blur when installed and falls back to the dim when it is not.

Dialog — A blurred background.
<Dialog>
  <Dialog.Trigger>
    <Button variant="outline">Discard changes</Button>
  </Dialog.Trigger>
  <Dialog.Content blur>
    <Dialog.Title>Leave without saving?</Dialog.Title>
    <Dialog.Description>Your changes will be lost.</Dialog.Description>
    <Dialog.Footer>
      <Dialog.Close><Button size="sm" variant="ghost">Keep editing</Button></Dialog.Close>
      <Dialog.Close><Button size="sm" variant="destructive">Discard</Button></Dialog.Close>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog>

Actions on their own surface

variant="panel" separates what the dialog says from what you can do about it.

Worth it on a dialog with a form in it, where the buttons are otherwise the last row of the form. A dialog that only asks a question does not need it — there is nothing there for the band to divide.

<Dialog>
  <Dialog.Trigger>
    <Button variant="outline">Edit profile</Button>
  </Dialog.Trigger>
  <Dialog.Content>
    <Dialog.Title>Edit profile</Dialog.Title>
    <Dialog.Description>Make changes to your profile here.</Dialog.Description>
    <View className="gap-4 py-4">
      <Input label="Name" defaultValue="Pedro Duarte" />
      <Input label="Username" defaultValue="@peduarte" />
    </View>
    <Dialog.Footer variant="panel">
      <Dialog.Close>
        <Button size="sm" variant="outline">Cancel</Button>
      </Dialog.Close>
      <Dialog.Close>
        <Button size="sm">Save changes</Button>
      </Dialog.Close>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog>

Variants

variant

  • plain (default)
  • panel
<Dialog variant="plain">…</Dialog>
<Dialog variant="panel">…</Dialog>

API Reference

Dialog

PropTypeDefaultDescription
openbooleanControlled open state.
onOpenChange(open: boolean) => void
defaultOpenbooleanInitial state when uncontrolled.

Dialog.Content

PropTypeDefaultDescription
classNamestring
dismissiblebooleantrueTap on the backdrop closes the dialog. Default true.
blurbooleanfalseFrost the screen behind the dialog instead of dimming it. Uses expo-blur when installed and falls back to the dim when it is not, so it is safe to pass either way. Someone who has Reduce Transparency switched on gets an opaque backdrop instead, which is the whole point of the setting.

Dialog.Footer

PropTypeDefaultDescription
classNamestring

Every part also accepts the underlying React Native props (ViewProps or TextProps) and a className for Tailwind utilities.

Notes

Focus after closing

On the web, closing returns keyboard focus to the element that had it before the overlay opened. Nested overlays return to the still-open parent first. If that element was removed or disabled while the overlay was open, it is skipped rather than focusing a stale control. Native screen-reader containment remains the platform's accessibilityViewIsModal behaviour.

Public exports

Values: Dialog

Types: DialogProps, DialogContentProps

On this page