Propel

Alert Dialog

A modal confirmation that requires an explicit user response before a destructive action.

Show code
import {
  AlertDialog,
  AlertDialogActions,
  AlertDialogBody,
  AlertDialogClose,
  AlertDialogCloseGroup,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogHeader,
  AlertDialogIcon,
  AlertDialogIntro,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@makeplane/propel/components/alert-dialog";
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { X } from "lucide-react";

export default function BasicDemo() {
  return (
    <AlertDialog>
      <Button
        stretch="auto"
        variant="danger"
        size="md"
        render={<AlertDialogTrigger />}
        label="Delete project"
      />
      <AlertDialogContent>
        <AlertDialogBody>
          <AlertDialogHeader>
            <AlertDialogIcon variant="danger" />
            <AlertDialogIntro>
              <AlertDialogTitle>Delete project?</AlertDialogTitle>
              <AlertDialogDescription>
                This permanently removes the project and all of its work items. This action
                can&apos;t be undone.
              </AlertDialogDescription>
            </AlertDialogIntro>
          </AlertDialogHeader>
        </AlertDialogBody>
        <AlertDialogActions>
          <Button
            stretch="auto"
            variant="secondary"
            size="md"
            render={<AlertDialogClose />}
            label="Cancel"
          />
          <Button
            stretch="auto"
            variant="danger"
            size="md"
            render={<AlertDialogClose />}
            label="Delete"
          />
        </AlertDialogActions>
        <AlertDialogCloseGroup>
          <IconButton
            variant="ghost"
            size="xs"
            aria-label="Close"
            render={<AlertDialogClose />}
            icon={<Icon icon={X} />}
          />
        </AlertDialogCloseGroup>
      </AlertDialogContent>
    </AlertDialog>
  );
}

Installation

import {
  AlertDialog,
  AlertDialogTrigger,
  AlertDialogContent,
  AlertDialogBody,
  AlertDialogHeader,
  AlertDialogIcon,
  AlertDialogIntro,
  AlertDialogTitle,
  AlertDialogDescription,
  AlertDialogActions,
  AlertDialogCloseGroup,
  AlertDialogClose,
} from "@makeplane/propel/components/alert-dialog";

Usage

Compose a 600px card: AlertDialogBody stacks the intent badge (AlertDialogIcon) above the title/description (AlertDialogIntro), optional fields go in the body under the header, and AlertDialogActions is the bordered footer. Pin a corner dismiss with AlertDialogCloseGroup + IconButton rendering AlertDialogClose.

import {
  AlertDialog,
  AlertDialogActions,
  AlertDialogBody,
  AlertDialogClose,
  AlertDialogCloseGroup,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogHeader,
  AlertDialogIcon,
  AlertDialogIntro,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@makeplane/propel/components/alert-dialog";
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { X } from "lucide-react";

export default function BasicDemo() {
  return (
    <AlertDialog>
      <Button
        stretch="auto"
        variant="danger"
        size="md"
        render={<AlertDialogTrigger />}
        label="Delete project"
      />
      <AlertDialogContent>
        <AlertDialogBody>
          <AlertDialogHeader>
            <AlertDialogIcon variant="danger" />
            <AlertDialogIntro>
              <AlertDialogTitle>Delete project?</AlertDialogTitle>
              <AlertDialogDescription>
                This permanently removes the project and all of its work items. This action
                can&apos;t be undone.
              </AlertDialogDescription>
            </AlertDialogIntro>
          </AlertDialogHeader>
        </AlertDialogBody>
        <AlertDialogActions>
          <Button
            stretch="auto"
            variant="secondary"
            size="md"
            render={<AlertDialogClose />}
            label="Cancel"
          />
          <Button
            stretch="auto"
            variant="danger"
            size="md"
            render={<AlertDialogClose />}
            label="Delete"
          />
        </AlertDialogActions>
        <AlertDialogCloseGroup>
          <IconButton
            variant="ghost"
            size="xs"
            aria-label="Close"
            render={<AlertDialogClose />}
            icon={<Icon icon={X} />}
          />
        </AlertDialogCloseGroup>
      </AlertDialogContent>
    </AlertDialog>
  );
}

Examples

Variants

The leading AlertDialogIcon badge sets the confirmation’s intent through its required variantdanger, warning, info, or success. It defaults to a filled status glyph; pass children to override. Use a danger primary action for destructive confirmations; other intents typically use primary.

Show code
import {
  AlertDialog,
  AlertDialogActions,
  AlertDialogBody,
  AlertDialogClose,
  AlertDialogCloseGroup,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogHeader,
  AlertDialogIcon,
  AlertDialogIntro,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@makeplane/propel/components/alert-dialog";
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { X } from "lucide-react";

export default function VariantsDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <AlertDialog>
        <Button
          stretch="auto"
          variant="danger"
          size="md"
          render={<AlertDialogTrigger />}
          label="Delete project"
        />
        <AlertDialogContent>
          <AlertDialogBody>
            <AlertDialogHeader>
              <AlertDialogIcon variant="danger" />
              <AlertDialogIntro>
                <AlertDialogTitle>Delete project?</AlertDialogTitle>
                <AlertDialogDescription>
                  This permanently removes the project and all of its work items. This action
                  can&apos;t be undone.
                </AlertDialogDescription>
              </AlertDialogIntro>
            </AlertDialogHeader>
          </AlertDialogBody>
          <AlertDialogActions>
            <Button
              stretch="auto"
              variant="secondary"
              size="md"
              render={<AlertDialogClose />}
              label="Cancel"
            />
            <Button
              stretch="auto"
              variant="danger"
              size="md"
              render={<AlertDialogClose />}
              label="Delete"
            />
          </AlertDialogActions>
          <AlertDialogCloseGroup>
            <IconButton
              variant="ghost"
              size="xs"
              aria-label="Close"
              render={<AlertDialogClose />}
              icon={<Icon icon={X} />}
            />
          </AlertDialogCloseGroup>
        </AlertDialogContent>
      </AlertDialog>

      <AlertDialog>
        <Button
          stretch="auto"
          variant="secondary"
          size="md"
          render={<AlertDialogTrigger />}
          label="Archive project"
        />
        <AlertDialogContent>
          <AlertDialogBody>
            <AlertDialogHeader>
              <AlertDialogIcon variant="warning" />
              <AlertDialogIntro>
                <AlertDialogTitle>Archive project?</AlertDialogTitle>
                <AlertDialogDescription>
                  Archiving hides the project from your sidebar. You can restore it later from
                  workspace settings.
                </AlertDialogDescription>
              </AlertDialogIntro>
            </AlertDialogHeader>
          </AlertDialogBody>
          <AlertDialogActions>
            <Button
              stretch="auto"
              variant="secondary"
              size="md"
              render={<AlertDialogClose />}
              label="Cancel"
            />
            <Button
              stretch="auto"
              variant="primary"
              size="md"
              render={<AlertDialogClose />}
              label="Archive"
            />
          </AlertDialogActions>
          <AlertDialogCloseGroup>
            <IconButton
              variant="ghost"
              size="xs"
              aria-label="Close"
              render={<AlertDialogClose />}
              icon={<Icon icon={X} />}
            />
          </AlertDialogCloseGroup>
        </AlertDialogContent>
      </AlertDialog>

      <AlertDialog>
        <Button
          stretch="auto"
          variant="secondary"
          size="md"
          render={<AlertDialogTrigger />}
          label="Transfer ownership"
        />
        <AlertDialogContent>
          <AlertDialogBody>
            <AlertDialogHeader>
              <AlertDialogIcon variant="info" />
              <AlertDialogIntro>
                <AlertDialogTitle>Transfer ownership?</AlertDialogTitle>
                <AlertDialogDescription>
                  Ownership of this project moves to another member. You&apos;ll keep your member
                  access.
                </AlertDialogDescription>
              </AlertDialogIntro>
            </AlertDialogHeader>
          </AlertDialogBody>
          <AlertDialogActions>
            <Button
              stretch="auto"
              variant="secondary"
              size="md"
              render={<AlertDialogClose />}
              label="Cancel"
            />
            <Button
              stretch="auto"
              variant="primary"
              size="md"
              render={<AlertDialogClose />}
              label="Transfer"
            />
          </AlertDialogActions>
          <AlertDialogCloseGroup>
            <IconButton
              variant="ghost"
              size="xs"
              aria-label="Close"
              render={<AlertDialogClose />}
              icon={<Icon icon={X} />}
            />
          </AlertDialogCloseGroup>
        </AlertDialogContent>
      </AlertDialog>

      <AlertDialog>
        <Button
          stretch="auto"
          variant="secondary"
          size="md"
          render={<AlertDialogTrigger />}
          label="Restore project"
        />
        <AlertDialogContent>
          <AlertDialogBody>
            <AlertDialogHeader>
              <AlertDialogIcon variant="success" />
              <AlertDialogIntro>
                <AlertDialogTitle>Restore project?</AlertDialogTitle>
                <AlertDialogDescription>
                  This brings the project back to your sidebar with all of its work items intact.
                </AlertDialogDescription>
              </AlertDialogIntro>
            </AlertDialogHeader>
          </AlertDialogBody>
          <AlertDialogActions>
            <Button
              stretch="auto"
              variant="secondary"
              size="md"
              render={<AlertDialogClose />}
              label="Cancel"
            />
            <Button
              stretch="auto"
              variant="primary"
              size="md"
              render={<AlertDialogClose />}
              label="Restore"
            />
          </AlertDialogActions>
          <AlertDialogCloseGroup>
            <IconButton
              variant="ghost"
              size="xs"
              aria-label="Close"
              render={<AlertDialogClose />}
              icon={<Icon icon={X} />}
            />
          </AlertDialogCloseGroup>
        </AlertDialogContent>
      </AlertDialog>
    </div>
  );
}

Open from menu

An alert dialog opened by a menu item is controlled through open / onOpenChange rather than an AlertDialogTrigger.

Show code
import {
  AlertDialog,
  AlertDialogActions,
  AlertDialogBody,
  AlertDialogClose,
  AlertDialogCloseGroup,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogHeader,
  AlertDialogIcon,
  AlertDialogIntro,
  AlertDialogTitle,
} from "@makeplane/propel/components/alert-dialog";
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { Menu, MenuContent, MenuItem, MenuTrigger } from "@makeplane/propel/components/menu";
import { X } from "lucide-react";
import * as React from "react";

export default function OpenFromMenuDemo() {
  const [confirmOpen, setConfirmOpen] = React.useState(false);
  return (
    <>
      <Menu>
        <Button
          stretch="auto"
          variant="secondary"
          size="md"
          render={<MenuTrigger />}
          label="Project options"
        />
        <MenuContent>
          <MenuItem label="Rename" />
          <MenuItem label="Duplicate" />
          <MenuItem onClick={() => setConfirmOpen(true)} label="Delete project…" />
        </MenuContent>
      </Menu>
      <AlertDialog open={confirmOpen} onOpenChange={setConfirmOpen}>
        <AlertDialogContent>
          <AlertDialogBody>
            <AlertDialogHeader>
              <AlertDialogIcon variant="danger" />
              <AlertDialogIntro>
                <AlertDialogTitle>Delete project?</AlertDialogTitle>
                <AlertDialogDescription>
                  This permanently removes the project and all of its work items. This action
                  can&apos;t be undone.
                </AlertDialogDescription>
              </AlertDialogIntro>
            </AlertDialogHeader>
          </AlertDialogBody>
          <AlertDialogActions>
            <Button
              stretch="auto"
              variant="secondary"
              size="md"
              render={<AlertDialogClose />}
              label="Cancel"
            />
            <Button
              stretch="auto"
              variant="danger"
              size="md"
              render={<AlertDialogClose />}
              label="Delete"
            />
          </AlertDialogActions>
          <AlertDialogCloseGroup>
            <IconButton
              variant="ghost"
              size="xs"
              aria-label="Close"
              render={<AlertDialogClose />}
              icon={<Icon icon={X} />}
            />
          </AlertDialogCloseGroup>
        </AlertDialogContent>
      </AlertDialog>
    </>
  );
}

Multiple triggers

createAlertDialogHandle connects many detached triggers to one shared confirmation, each passing the project it acts on as its payload.

Show code
import {
  AlertDialog,
  AlertDialogActions,
  AlertDialogBody,
  AlertDialogClose,
  AlertDialogCloseGroup,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogHeader,
  AlertDialogIcon,
  AlertDialogIntro,
  AlertDialogTitle,
  AlertDialogTrigger,
  createAlertDialogHandle,
} from "@makeplane/propel/components/alert-dialog";
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { X } from "lucide-react";

// One shared confirmation driven by detached triggers: each trigger passes the project it acts on
// as its `payload`, and the root reads that payload to personalize the copy.
const deleteProjectHandle = createAlertDialogHandle();

export default function MultipleTriggersDemo() {
  return (
    <>
      <div className="flex flex-wrap items-center gap-3">
        {["Mobile app", "Design system"].map((project) => (
          <Button
            key={project}
            stretch="auto"
            variant="secondary"
            size="md"
            render={<AlertDialogTrigger handle={deleteProjectHandle} payload={project} />}
            label={`Delete ${project}`}
          />
        ))}
      </div>
      <AlertDialog handle={deleteProjectHandle}>
        {({ payload }: { payload: unknown }) => (
          <AlertDialogContent>
            <AlertDialogBody>
              <AlertDialogHeader>
                <AlertDialogIcon variant="danger" />
                <AlertDialogIntro>
                  <AlertDialogTitle>
                    Delete {typeof payload === "string" ? payload : "this project"}?
                  </AlertDialogTitle>
                  <AlertDialogDescription>
                    This permanently removes the project and all of its work items. This action
                    can&apos;t be undone.
                  </AlertDialogDescription>
                </AlertDialogIntro>
              </AlertDialogHeader>
            </AlertDialogBody>
            <AlertDialogActions>
              <Button
                stretch="auto"
                variant="secondary"
                size="md"
                render={<AlertDialogClose />}
                label="Cancel"
              />
              <Button
                stretch="auto"
                variant="danger"
                size="md"
                render={<AlertDialogClose />}
                label="Delete"
              />
            </AlertDialogActions>
            <AlertDialogCloseGroup>
              <IconButton
                variant="ghost"
                size="xs"
                aria-label="Close"
                render={<AlertDialogClose />}
                icon={<Icon icon={X} />}
              />
            </AlertDialogCloseGroup>
          </AlertDialogContent>
        )}
      </AlertDialog>
    </>
  );
}

API Reference

AlertDialog

The alert dialog Root — Base UI's context/state provider (renders no element of its own). Always modal and non-dismissible, so it requires an explicit user response. A behavior-only role, so it lives in `components` (rules 1a, 2); the styled parts live in `elements/alert-dialog` and are grafted onto Base UI behavior here.

PropTypeDefaultDescription
childrenReactNode | PayloadChildRenderFunction<unknown>The content of the dialog. This can be a regular React node or a render function that receives the `payload` of the active trigger.

AlertDialogTrigger

The behavior that opens the alert dialog. Use as the `render` target of a `Button` so the styled primitive's look wins via render-composition: ```tsx <Button variant="danger" size="lg" label="Delete project" render={<AlertDialogTrigger />} />; ``` Base UI manages `aria-haspopup`/`aria-expanded` and focus restoration when the alert dialog closes. Maps 1:1 to `AlertDialog.Trigger`.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogTriggerState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.

AlertDialogContent

Convenience that composes the alert dialog overlay boilerplate — Base UI portal, the shared `internal` backdrop, the centering viewport, and the centered popup — so a consumer only writes the trigger and the popup body.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.

AlertDialogBody

The padded content region of the alert dialog popup. Stacks the header (icon + intro) and any optional body content (form fields, extra copy) with a 16px gap. Caps height so tall content scrolls while the actions footer stays pinned. `tabIndex={0}` by default so a keyboard user can scroll a body with no other focusable content (WCAG 2.1.1).

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.

AlertDialogHeader

Groups the leading `AlertDialogIcon` badge above the `AlertDialogIntro` (title + description). Lives inside `AlertDialogBody`. Pass icon and intro as children.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.

AlertDialogIcon

The leading intent badge above the title. Soft tinted surface + filled status glyph selected from `variant` — callers never pass an icon (matches `ToastStatusIcon`). Decorative (`aria-hidden`) — the title carries the accessible name. `variant` is required (`danger` | `warning` | `info` | `success`).

PropTypeDefaultDescription
variant(required)"danger" | "warning" | "info" | "success"
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.

AlertDialogIntro

Groups the title and description with a 6px gap. Pass `AlertDialogTitle` and `AlertDialogDescription` as children.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.

AlertDialogTitle

The accessible title for the alert dialog. Base UI links it to the popup via `aria-labelledby`; the styled `elements` title supplies the h5/medium look.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogTitleState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.

AlertDialogDescription

The supporting description for the alert dialog. Base UI links it to the popup via `aria-describedby`; the styled `elements` description supplies the body-xs / tertiary look.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogDescriptionState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.

AlertDialogActions

The footer row of action buttons. Draws a top border, pads to match the body gutter, and right-aligns children with a 12px gap.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.

AlertDialogCloseGroup

Positions the corner dismiss control at the popup's top-end. Pass an `IconButton` (components tier) or the styled `AlertDialogClose` (elements showcase) as the child.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.

AlertDialogClose

The behavior that closes the alert dialog when activated. Use as the `render` target of a `Button` so the styled primitive's look wins via render-composition: ```tsx <Button variant="secondary" size="lg" label="Cancel" render={<AlertDialogClose />} />; ``` Maps 1:1 to `AlertDialog.Close`.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogCloseState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.