<Dialog.Root>
<Dialog.Trigger render={(p) => <Button {...p}>Open Dialog</Button>} />
<Dialog className="p-8">
  <div className="flex items-start justify-between gap-4 mb-4">
    <Dialog.Title className="text-2xl font-semibold">
      Modal Title
    </Dialog.Title>
    <Dialog.Close
      aria-label="Close"
      render={(props) => (
        <Button
          {...props}
          variant="secondary"
          shape="square"
          icon={<X />}
        />
      )}
    />
  </div>
  <Dialog.Description className="text-kumo-subtle">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  </Dialog.Description>
  <div className="mt-8 flex justify-end gap-2">
    <Button variant="secondary">Cancel</Button>
    <Dialog.Close
      render={(props) => (
        <Button variant="destructive" {...props}>
          Delete
        </Button>
      )}
    />
  </div>
</Dialog>
</Dialog.Root>

Installation

Barrel

import { Dialog } from "@cloudflare/kumo";

Granular

import { Dialog } from "@cloudflare/kumo/components/dialog";

Usage

import { Dialog, Button } from "@cloudflare/kumo";

export default function Example() {
return (
  <Dialog.Root>
    <Dialog.Trigger render={(p) => <Button {...p}>Open</Button>} />
    <Dialog>
      <Dialog.Title>Dialog Title</Dialog.Title>
      <Dialog.Description>
        Dialog content goes here.
      </Dialog.Description>
      <div className="flex justify-end gap-2 mt-4">
        <Dialog.Close
          render={(p) => (
            <Button variant="secondary" {...p}>
              Cancel
            </Button>
          )}
        />
      </div>
    </Dialog>
  </Dialog.Root>
);
}

Dialog vs Alert Dialog

The Dialog component supports two ARIA roles to properly convey semantic meaning to assistive technologies:

RoleUse CaseBehavior

role="dialog" (default)

General-purpose modals, forms, content displayDismissible by default

role="alertdialog"

Destructive actions, confirmations, critical warningsRequires explicit user acknowledgment
// General-purpose dialog
<Dialog.Root>
<Dialog.Trigger render={(p) => <Button {...p}>Edit Profile</Button>} />
<Dialog>{/* Form content */}</Dialog>
</Dialog.Root>

// Alert dialog for destructive actions

<Dialog.Root role="alertdialog">
<Dialog.Trigger render={(p) => <Button variant="destructive" {...p}>Delete</Button>} />
<Dialog>{/* Confirmation content */}</Dialog>
</Dialog.Root>

Examples

Basic Dialog

<Dialog.Root>
<Dialog.Trigger render={(p) => <Button {...p}>Click me</Button>} />
<Dialog className="p-8">
  <div className="flex items-start justify-between gap-4 mb-4">
    <Dialog.Title className="text-2xl font-semibold">
      Modal Title
    </Dialog.Title>
    <Dialog.Close
      aria-label="Close"
      render={(props) => (
        <Button
          {...props}
          variant="secondary"
          shape="square"
          icon={<X />}
        />
      )}
    />
  </div>
  <Dialog.Description className="text-kumo-subtle">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  </Dialog.Description>
</Dialog>
</Dialog.Root>

Alert Dialog (role=“alertdialog”)

For destructive or confirmation dialogs, use role="alertdialog" on Dialog.Root. This provides proper accessibility semantics by rendering the dialog with role="alertdialog" instead of role="dialog".

When to use role=“alertdialog”:

  • Destructive actions (delete, discard, remove)
  • Confirmation flows requiring explicit user acknowledgment
  • Actions that cannot be undone
  • Critical warnings or errors
<Dialog.Root role="alertdialog">

<Dialog.Trigger
  render={(p) => (
    <Button {...p} variant="destructive">Delete Account</Button>
  )}
/>
<Dialog className="p-8">
  <div className="mb-4 flex items-center gap-3">
    <div className="flex h-10 w-10 items-center justify-center rounded-full bg-kumo-danger/20">
      <Warning size={20} className="text-kumo-danger" weight="fill" />
    </div>
    <Dialog.Title className="text-xl font-semibold">
      Delete Account?
    </Dialog.Title>
  </div>
  <Dialog.Description className="text-kumo-subtle">
    This action cannot be undone. All your data will be permanently
    removed from our servers. Are you sure you want to proceed?
  </Dialog.Description>
  <div className="mt-8 flex justify-end gap-2">
    <Dialog.Close
      render={(props) => (
        <Button variant="secondary" {...props}>Cancel</Button>
      )}
    />
    <Dialog.Close
      render={(props) => (
        <Button variant="destructive" {...props}>Delete Account</Button>
      )}
    />
  </div>
</Dialog>
</Dialog.Root>

Confirmation Dialog (with disablePointerDismissal)

For confirmation dialogs that should not be dismissed by clicking outside, use disablePointerDismissal on Dialog.Root. This can be combined with role="alertdialog" for proper accessibility.

<Dialog.Root disablePointerDismissal>

<Dialog.Trigger
  render={(p) => (
    <Button {...p} variant="destructive">Delete Project</Button>
  )}
/>
<Dialog className="p-8">
  <div className="mb-4 flex items-center gap-3">
    <div className="flex h-10 w-10 items-center justify-center rounded-full bg-kumo-danger/20">
      <Warning size={20} className="text-kumo-danger" />
    </div>
    <Dialog.Title className="text-xl font-semibold">
      Delete Project?
    </Dialog.Title>
  </div>
  <Dialog.Description className="text-kumo-subtle">
    This action cannot be undone. This will permanently
    delete the project and all associated data.
  </Dialog.Description>
  <div className="mt-8 flex justify-end gap-2">
    <Dialog.Close
      render={(props) => (
        <Button variant="secondary" {...props}>Cancel</Button>
      )}
    />
    <Dialog.Close
      render={(props) => (
        <Button variant="destructive" {...props}>Delete</Button>
      )}
    />
  </div>
</Dialog>
</Dialog.Root>

With Actions

<Dialog.Root>

<Dialog.Trigger render={(p) => <Button {...p}>Delete</Button>} />
<Dialog className="p-8">
  <div className="flex items-start justify-between gap-4 mb-4">
    <Dialog.Title className="text-2xl font-semibold">
      Modal Title
    </Dialog.Title>
    <Dialog.Close
      aria-label="Close"
      render={(props) => (
        <Button
          {...props}
          variant="secondary"
          shape="square"
          icon={<X />}
        />
      )}
    />
  </div>
  <Dialog.Description className="text-kumo-subtle">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  </Dialog.Description>
  <div className="mt-8 flex justify-end gap-2">
    <Button variant="secondary">Cancel</Button>
    <Dialog.Close
      render={(props) => (
        <Button variant="destructive" {...props}>
          Delete
        </Button>
      )}
    />
  </div>
</Dialog>
</Dialog.Root>

With Select

Dialog containing a Select dropdown.

<Dialog.Root>

<Dialog.Trigger render={(p) => <Button {...p}>Open Form</Button>} />
<Dialog className="p-8">
  <Dialog.Title>Create Resource</Dialog.Title>
  <Dialog.Description>Select a region for your new resource.</Dialog.Description>
  <Select className="w-full">
    <Select.Option value="us-east">US East</Select.Option>
    <Select.Option value="us-west">US West</Select.Option>
    <Select.Option value="eu-west">EU West</Select.Option>
  </Select>
</Dialog>
</Dialog.Root>

With Combobox

Dialog containing a Combobox for searchable selection.

<Dialog.Root>

<Dialog.Trigger render={(p) => <Button {...p}>Open Form</Button>} />
<Dialog className="p-8">
  <Dialog.Title>Create Resource</Dialog.Title>
  <Dialog.Description>Search and select a region.</Dialog.Description>
  <Combobox value={value} onValueChange={setValue} items={regions}>
    <Combobox.TriggerInput placeholder="Search regions..." />
    <Combobox.Content>
      <Combobox.List>
        {(item) => <Combobox.Item value={item}>{item.label}</Combobox.Item>}
      </Combobox.List>
    </Combobox.Content>
  </Combobox>
</Dialog>
</Dialog.Root>

With Dropdown

Dialog containing a Dropdown menu.

<Dialog.Root>

<Dialog.Trigger render={(p) => <Button {...p}>Open Form</Button>} />
<Dialog className="p-8">
  <Dialog.Title>Resource Actions</Dialog.Title>
  <Dialog.Description>Choose an action.</Dialog.Description>
  <DropdownMenu>
    <DropdownMenu.Trigger render={<Button>Actions</Button>} />
    <DropdownMenu.Content>
      <DropdownMenu.Item>Edit</DropdownMenu.Item>
      <DropdownMenu.Item>Duplicate</DropdownMenu.Item>
      <DropdownMenu.Separator />
      <DropdownMenu.Item variant="danger">Delete</DropdownMenu.Item>
    </DropdownMenu.Content>
  </DropdownMenu>
</Dialog>
</Dialog.Root>

API Reference

Dialog

The main dialog container that renders the modal overlay and popup.

PropTypeDefaultDescription
classNamestring-Additional CSS classes merged via `cn()`.
childrenReactNode-Dialog content (typically Title, Description, Close, and action buttons).
size"base" | "sm" | "lg" | "xl""base"Dialog width. - `"sm"` — Small (min 288px) for simple confirmations - `"base"` — Default (min 384px) - `"lg"` — Large (min 512px) for complex content - `"xl"` — Extra large (min 768px) for detailed views

Dialog.Root

Controls the open state of the dialog. Doesn’t render its own HTML element.

PropTypeDefaultDescription
role”dialog” | “alertdialog""dialog”

The ARIA role for the dialog. Use "alertdialog" for destructive or confirmation flows.

disablePointerDismissalbooleanfalse

When true, prevents the dialog from being dismissed by clicking outside.

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

Dialog.Trigger

A button that opens the dialog when clicked.

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

Dialog.Title

A heading that labels the dialog for accessibility.

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

Dialog.Description

A paragraph providing additional context about the dialog.

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

Dialog.Close

A button that closes the dialog when clicked.

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.