ConfirmActionModal
Opinionated confirmation dialog for destructive or high-risk actions, with helper patterns for buttons and menus.
Overview
ConfirmActionModal is the shared-ui confirmation dialog used when an action should require explicit user confirmation before it executes.
The file also exports ConfirmActionButton, which is a convenience wrapper that renders a trigger and opens ConfirmActionModal for you.
In practice, there are three main ways this shows up in the platform:
- raw controlled
ConfirmActionModal ConfirmActionButtonfor inline actionsMenu.ItemWithConfirmationfor menu-driven destructive actions
Raw Modal Usage
Use ConfirmActionModal directly when the feature already owns modal state or needs custom follow-up behavior:
<ConfirmActionModal
isOpen={isOpen}
handleClose={() => setIsOpen(false)}
onConfirm={handleDelete}
title="Delete collection?"
description="This action cannot be undone."
buttonLabel="Yes, delete"
buttonVariant="destructive"
isPending={isDeleting}
/>This is the most flexible option.
ConfirmActionButton
Use ConfirmActionButton when the trigger and the confirmation belong together:
<ConfirmActionButton
title={`Delete "${event.subject}"?`}
description="This event will be deleted permanently."
variant="destructive"
onConfirm={handleDelete}
isPending={isDeleting}
>
Delete Event
</ConfirmActionButton>This is useful in forms, sidebars, and panel footers where a single destructive button should open a confirmation modal.
Menu Integration
For menu-driven destructive actions, prefer Menu.ItemWithConfirmation rather than wiring menu state and a sibling modal yourself:
<Menu.ItemWithConfirmation
label="Delete"
icon="TrashCan02"
variant="destructive"
onSelect={handleDelete}
confirmTitle="Delete item?"
confirmDescription="This action cannot be undone."
confirmButtonText="Delete"
/>Menu.ItemWithConfirmation already handles opening the confirmation modal and closing the menu correctly.
Props
ConfirmActionModal
| Prop | Type | Default | Description |
|---|---|---|---|
isOpen | boolean | — | Required. Controls visibility. |
handleClose | () => void | — | Required. Closes the modal. |
onConfirm | () => void | Promise<void> | — | Required confirm handler. Awaited before close. |
title | string | delete-focused copy | Confirmation title. |
description | string | delete-focused copy | Confirmation body copy. |
isPending | boolean | — | Loading state for the confirm action. |
buttonLabel | string | 'Yes, delete' | Confirm button text. |
buttonVariant | ButtonVariant | 'destructive' | Confirm button style. |
children | ReactNode | — | Extra modal body content below the description. |
onSuccess | () => void | — | Called after a successful confirm and close. |
ConfirmActionButton
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Required trigger content. |
onConfirm | () => void | — | Required confirm handler. |
title | string | — | Modal title override. |
description | string | — | Modal description override. |
size | ButtonProps['size'] | — | Trigger button size. |
variant | ButtonProps['variant'] | — | Trigger button variant. |
isPending | boolean | — | Loading state for the trigger and modal action. |
skipConfirmation | boolean | false | Runs onConfirm immediately without opening the modal. |
disabled | boolean | — | Disables the trigger. |
confirmButtonLabel | string | — | Override for the modal confirm button text. |
asChild | boolean | false | Uses the child as the visual trigger instead of rendering a Button. |
className | string | — | Additional trigger classes. |
modalChildren | ReactNode | — | Extra content rendered inside the modal. |
Brand Dashboard Usage Patterns
Representative usages:
| Pattern | Path | Notes |
|---|---|---|
| Non-delete destructive reset | fsai/apps/brand-dashboard/src/pages/Workflows/components/config-panel/TriggerConfig.tsx | Good example of overriding the delete-focused default copy. |
| Inline destructive button | fsai/apps/brand-dashboard/src/pages/Home/Calendar/CalendarEventInformation/CalendarEventForm.tsx | Canonical ConfirmActionButton usage in a form footer. |
| Menu-native confirmation | fsai/apps/brand-dashboard/src/modules/locations/components/LocationPanel/LocationPanel.tsx | Uses Panel.Menu.ItemWithConfirmation for destructive panel actions. |
| Manual menu + modal state | fsai/apps/brand-dashboard/src/modules/library/components/CollectionDropdown.tsx | Shows the more manual pattern when the menu item just opens local modal state. |
| Controlled deletion flow | fsai/apps/brand-dashboard/src/pages/Learning/components/CoursesTab.tsx | Standard raw ConfirmActionModal pattern for content deletion. |
Important Conventions
- The default
title,description, and button label are delete-focused. Override them for non-delete confirmations. onConfirmis awaited. If it throws, the modal stays open andonSuccessdoes not run.- Prefer
Menu.ItemWithConfirmationwhen the action originates from a menu. - Prefer
ConfirmActionButtonwhen a single trigger button and its confirmation belong together. - Use raw
ConfirmActionModalwhen the feature already owns state or needs multiple confirmation flows.
Guidelines
- Do use
ConfirmActionModalfor destructive or high-risk actions - Do customize the copy when the action is not literally deleting something
- Do show a loading state while the confirmation action is in flight
- Don't build a custom one-off modal when this abstraction already fits
- Don't use the raw delete defaults for non-delete confirmations