ConfirmDialog | Dashforge-UI
DocsStarter Kits
v0.1.0-alpha

ConfirmDialog

An imperative confirmation dialog with promise-based API for simple user confirmation flows. Provides a quick alternative to MUI Dialog for standard confirmation patterns.

Imperative Pattern

Quick Start

Two-step setup: wrap your app with the provider and call the hook

Quick Start

2 Steps

Step 1: Wrap your app with the provider

import { ConfirmDialogProvider } from '@dashforge/ui';

<ConfirmDialogProvider>
  <App />
</ConfirmDialogProvider>

Step 2: Use the hook in your components

import { useConfirm } from '@dashforge/ui';

const confirm = useConfirm();

const handleDelete = async () => {
  const result = await confirm({
    title: 'Delete User',
    description: 'This action cannot be undone.',
  });

  if (result.status === 'confirmed') {
    await deleteUser();
  }
};

Examples

Common ConfirmDialog patterns and configurations

Basic Confirmation

Simple confirm with title and description

Live Demo
const confirm = useConfirm();

const handleDelete = async () => {
  const result = await confirm({
    title: 'Delete User',
    description: 'This action cannot be undone.',
  });

  if (result.status === 'confirmed') {
    await deleteUser();
  }
};
Custom Buttons

Customize button labels and colors

Live Demo
const result = await confirm({
  title: 'Proceed with Payment?',
  description: 'You will be charged $99.99 immediately.',
  confirmText: 'Pay Now',
  cancelText: 'Go Back',
  confirmButtonProps: {
    color: 'success',
  },
});

if (result.status === 'confirmed') {
  await processPayment();
}
Async Action Flow

Handle async operations after confirmation

Live Demo
const [loading, setLoading] = useState(false);

const handleSubmit = async () => {
  const result = await confirm({
    title: 'Submit Report?',
    description: 'This will notify all stakeholders.',
  });

  if (result.status === 'confirmed') {
    setLoading(true);
    await api.submitReport(data);
    setLoading(false);
  }
};
Form Dirty Check

Guard navigation with unsaved changes check

Live Demo

Form status: Has unsaved changes

const handleNavigate = async () => {
  if (!isDirty) {
    navigate('/next');
    return;
  }

  const result = await confirm({
    title: 'Discard Changes?',
    description: 'You have unsaved changes.',
    confirmText: 'Discard',
    cancelText: 'Stay',
  });

  if (result.status === 'confirmed') {
    navigate('/next');
  }
};

Understanding the Result

How to handle the discriminated union result from confirm()

The confirm function returns a semantic result that explicitly distinguishes between user actions and system behavior. Most of the time, you only need to check for confirmed status.

Recommended Pattern (90% of cases)

Check for confirmed, abort everything else

const result = await confirm({ title: 'Delete?' });

if (result.status === 'confirmed') {
  // User confirmed - proceed
  await performAction();
}
// Everything else = abort (simple!)
Result Types

confirmed

User clicked confirm button

cancelled

User cancelled via button, backdrop, or ESC key

blocked

Another dialog is already open (re-entrant call)

Advanced: Track cancellation reasons

if (result.status === 'cancelled') {
  analytics.track('dialog_cancelled', {
    reason: result.reason // 'cancel-button', 'backdrop', 'escape-key'
  });
}

Integration Scenarios

Real-world integration patterns with navigation guards and async actions

Navigation Guard

Prevent navigation away from forms with unsaved changes

Live Preview

Form status: Has unsaved changes

const handleNavigate = async () => {
  if (!isDirty) {
    navigate('/next');
    return;
  }

  const result = await confirm({
    title: 'Discard Changes?',
    description: 'You have unsaved changes.',
    confirmText: 'Discard',
    cancelText: 'Stay',
  });

  if (result.status === 'confirmed') {
    navigate('/next');
  }
};
Form Submission

Confirm before submitting critical forms

Live Preview

Example pattern below

const onSubmit = async (data: FormData) => {
  const result = await confirm({
    title: 'Submit Report?',
    description: 'This will send notifications to all stakeholders.',
  });

  if (result.status === 'confirmed') {
    setSubmitting(true);
    await api.submitReport(data);
    toast.success('Report submitted');
    navigate('/dashboard');
  }
};
Delete with Async Cleanup

Delete resource and clean up related data

Live Preview

Example pattern below

const handleDelete = async (projectId: string) => {
  const result = await confirm({
    title: 'Delete Project?',
    description: 'This will permanently delete all associated data.',
    confirmText: 'Delete',
    confirmButtonProps: { color: 'error' },
  });

  if (result.status === 'confirmed') {
    setDeleting(true);
    await api.deleteProject(projectId);
    await api.cleanupRelatedData(projectId);
    toast.success('Project deleted');
    queryClient.invalidateQueries(['projects']);
  }
};

API Reference

Complete API for ConfirmDialogProvider, useConfirm, and types

ConfirmDialogProvider

Context provider that enables useConfirm() hook. Wrap your app root or a subtree where you need confirmation dialogs.

PropTypeDescription
childrenReactNodeApp content to wrap
useConfirm()

Hook that returns the confirm function. Throws error if called outside ConfirmDialogProvider.

Returns

(options: ConfirmOptions) => Promise<ConfirmResult>

Imperative function that opens a confirmation dialog and returns a promise that resolves with the result.

ConfirmOptions
PropTypeRequiredDefaultDescription
titlestringYes-Dialog title
descriptionstring | ReactNodeNo-Dialog body content
confirmTextstringNo'Confirm'Confirm button label
cancelTextstringNo'Cancel'Cancel button label
confirmButtonPropsButtonPropsNo-Confirm button customization (color, variant, icon)
cancelButtonPropsButtonPropsNo-Cancel button customization
dialogPropsDialogPropsNo-MUI Dialog props (limited subset: maxWidth, fullWidth, fullScreen)
ConfirmResult

Discriminated union that distinguishes between user actions and system behavior.

type ConfirmResult =
  | { status: 'confirmed' }
  | {
      status: 'cancelled';
      reason: 'cancel-button' | 'backdrop' | 'escape-key' | 'provider-unmount';
    }
  | {
      status: 'blocked';
      reason: 'reentrant-call';
    };

Implementation Notes

Technical details, best practices, and usage guidelines

1

When NOT to Use ConfirmDialog

ConfirmDialog is designed for simple confirmation flows with standard layouts. For custom layouts, complex forms, or multi-step wizards, use MUI Dialog directly. ConfirmDialog provides a quick imperative API for common cases—it is NOT a replacement for all dialog use cases.

2

Re-entrancy Policy

Only one confirmation can be active at a time. Calling confirm() while another confirmation is pending will reject the previous promise with "cancelled" and immediately show the new confirmation. This prevents stacking multiple dialogs.

3

Promise Behavior

The confirm() hook returns a discriminated union result, not a boolean. Always check result.confirmed before proceeding. Cancelled confirmations return { confirmed: false, reason: "cancelled" } or { confirmed: false, reason: "closed" }.

4

Provider Scope

Wrap ConfirmDialogProvider at the highest level where confirmations are needed (typically at the app root or route layout). The useConfirm() hook must be called within a component tree wrapped by the provider.

5

Provider Unmount Behavior

If the provider unmounts while a confirmation is pending, the promise resolves with { confirmed: false, reason: "cancelled" }. This ensures cleanup and prevents memory leaks during navigation or conditional rendering.

6

Async Action Handling

For async actions (like API calls), handle loading states explicitly in your calling code. ConfirmDialog confirms user intent—it does not manage async execution. Show loading indicators or disable UI after confirmation as needed.

7

Form Integration Pattern

When using with forms, combine with form dirty state checks to guard navigation or unsaved changes. The confirmation should be triggered by your logic (e.g., beforeunload, navigation guard) rather than by the form itself.


On This Page