Skip to content

Dialog Patterns

Consistent modal and dialog patterns across the application.

Overview

flowchart TB
    subgraph Dialogs
        A[ConfirmDialog]
        B[FormDialog]
        C[EntityDialog]
    end

    subgraph System
        D[UnsavedChangesDialog]
        E[PrivacyNoticeDialog]
        F[PwaUpdateDialog]
    end

    subgraph Hooks
        G[useModal]
        H[useEntityModal]
        I[useFormModal]
    end

    G --> A
    H --> C
    I --> B
    I --> D

Confirmation Dialog

For destructive actions requiring user confirmation.

<ConfirmDeleteDialog
  open={isOpen}
  title={t("deleteMarker")}
  message={t("deleteMarkerConfirm")}
  onConfirm={handleDelete}
  onCancel={close}
  isLoading={isDeleting}
/>

Pattern: - Clear title stating the action - Message explaining consequences - Cancel as default/safe option - Loading state during async action

Form Dialog

Dialog containing a form with validation.

<FormDialog
  open={isOpen}
  title={entity ? t("editMarker") : t("createMarker")}
  onClose={handleClose}
>
  <MarkerForm
    defaultValues={entity}
    onSubmit={handleSubmit}
    onCancel={close}
  />
</FormDialog>

Features: - Dirty state tracking - Unsaved changes warning on close - Form reset on close - Loading state during submission

Entity Dialog

Full CRUD dialog for entity management.

const { isOpen, entity, openCreate, openEdit, close } = useEntityModal<Category>();

<EntityDialog
  open={isOpen}
  entity={entity}
  onSave={handleSave}
  onDelete={handleDelete}
  onClose={close}
/>

Unsaved Changes Dialog

Warns users before discarding form changes.

<UnsavedChangesDialog
  open={showWarning}
  onDiscard={() => {
    resetForm();
    close();
  }}
  onCancel={() => setShowWarning(false)}
/>

Triggered by: - Closing form with dirty state - Navigating away with unsaved changes - Browser back button

Privacy Notice Dialog

First-run privacy consent.

const { hasAccepted, accept } = usePrivacyNoticeStore();

{!hasAccepted && (
  <PrivacyNoticeDialog
    open={true}
    onAccept={accept}
  />
)}

Persisted in localStorage.

PWA Update Dialog

Notifies users of available updates.

const { needsUpdate, update } = usePwaUpdate();

<PwaUpdateDialog
  open={needsUpdate}
  onUpdate={update}
  onDismiss={dismiss}
/>

Dialog Best Practices

Do Don't
Use clear action verbs Use vague "OK/Cancel"
Show loading states Allow double-submission
Trap focus inside dialog Allow background interaction
Support Escape to close Require mouse to close
Animate open/close Instant show/hide

Accessibility

Dialogs must: - Trap focus within the dialog - Return focus on close - Support Escape key - Have proper ARIA attributes

MUI Dialog handles this automatically:

<Dialog
  open={open}
  onClose={onClose}
  aria-labelledby="dialog-title"
>
  <DialogTitle id="dialog-title">Title</DialogTitle>
  ...
</Dialog>