apx-dsv0.1Local renderer · live
K
Foundations4
  • Getting started
  • Theming
  • Templates
  • Icons
  • Avatar
  • Badge
  • DataGrid
  • Scheduler
  • Stat
  • Table
  • Timeline
  • TreeView
  • Accordion
  • Alert
  • EmptyState
  • Progress
  • Skeleton
  • Spinner
  • SplashScreen
  • Toast
  • ColorPicker
  • FileUpload
  • Form
  • Rating
  • TagsInput
  • Combobox
  • Field
  • Select
  • Toggle
  • Button
  • Calendar
  • Checkbox
  • DatePicker
  • Input
  • NumberInput
  • Radio
  • Slider
  • Switch
  • Textarea
  • AppShell
  • Div
  • Divider
  • Sidebar
  • Stack
  • Typography
  • Image
  • Breadcrumbs
  • Carousel
  • NavigationMenu
  • Pagination
  • Stepper
  • Tabs
  • Toolbar
  • CommandPalette
  • Confirm
  • Drawer
  • HoverCard
  • Menu
  • Modal
  • Popover
  • Tooltip
  • Icon
  • Card
  • PricingCard
60 componentsapx-ds/renderer
Confirm
Variant↳ other

Overlays

Confirm

Imperative confirm dialog. Call

Confirm

A native-feeling confirm dialog that you don't render as JSX. Mount the singleton <ConfirmProvider /> once at your app root (next to <ThemeProvider />) and call the imperative confirm.display({ ... }) facade from anywhere — a click handler, an interceptor, an async action. The call returns a Promise<boolean> that resolves to true when the user confirms and false when they cancel, press Esc, or click the backdrop.

This mirrors the SplashProvider + splash(...) and Toaster + toast(...) patterns used elsewhere in the design system: the provider owns the portal and the lifecycle; consumers reach for a stateless function. No useState for open, no onConfirm / onCancel callbacks threaded through props — just an await.

tsx
// app/layout.tsx — mount once
import { ThemeProvider, ConfirmProvider } from '@apx-ui/ds';

export default function RootLayout({ children }) {
  return (
    <ThemeProvider>
      <ConfirmProvider /> {/* portal-owned, picks up theme via the surrounding provider */}
      {children}
    </ThemeProvider>
  );
}
// app/layout.tsx — mount once
import { ThemeProvider, ConfirmProvider } from '@apx-ui/ds';

export default function RootLayout({ children }) {
  return (
    <ThemeProvider>
      <ConfirmProvider /> {/* portal-owned, picks up theme via the surrounding provider */}
      {children}
    </ThemeProvider>
  );
}
tsx
// anywhere else — just call `confirm`
import { confirm } from '@apx-ui/ds';

async function onDelete() {
  const ok = await confirm.display({
    variant: 'error',
    title: 'Delete project?',
    description: 'This action cannot be undone.',
    confirmText: 'Yes, delete',
  });
  if (!ok) return;
  await deleteProject();
}

// or use the variant shortcut
const ok = await confirm.warning({ title: 'Reset filters?' });
// anywhere else — just call `confirm`
import { confirm } from '@apx-ui/ds';

async function onDelete() {
  const ok = await confirm.display({
    variant: 'error',
    title: 'Delete project?',
    description: 'This action cannot be undone.',
    confirmText: 'Yes, delete',
  });
  if (!ok) return;
  await deleteProject();
}

// or use the variant shortcut
const ok = await confirm.warning({ title: 'Reset filters?' });

Five variants — one button each

Loading preview…
Overview.tsx

Why imperative

A confirm dialog is conceptually outside the component tree that triggered it. A button in a settings page that wants to ask "are you sure?" shouldn't have to hoist open state to a sibling, render a <Modal /> somewhere, and thread callbacks through props. The same is true for non-React code paths (route guards, fetch interceptors, command-palette actions).

Instead, the provider lives at the root, owns the portal + focus trap + scroll lock once, and any caller gets a one-liner:

ts
const ok = await confirm.display({ /* ... */ });
if (!ok) return;
// do the thing
const ok = await confirm.display({ /* ... */ });
if (!ok) return;
// do the thing

The returned promise never rejects — false covers every dismiss path. That means you can treat the call as a pure boolean gate without try / catch boilerplate.

The imperative API

CallPurpose
confirm.display(options)Open the dialog. Returns Promise<boolean>.
confirm.info(options)Variant shortcut for { variant: 'info', ... }.
confirm.success(options)Variant shortcut for { variant: 'success', ... }.
confirm.warning(options)Variant shortcut for { variant: 'warning', ... }.
confirm.error(options)Variant shortcut for { variant: 'error', ... }.
confirm.cancel()Dismiss the active dialog (resolves with false). No-op if closed.
confirm.isOpen()true if a confirm dialog is currently visible.

Calling confirm.display(...) while a dialog is already open resolves the previous promise with false (treating the displaced dialog as cancelled) before the new record takes over. This matches how a blocking native window.confirm() would have prevented the second call from running, but without the UI freeze.

The minimal call

Loading preview…
Basic.tsx

Variants

VariantPalette roleDefault iconConfirm button colorReach for it when…
defaultneutralMessageCircleprimaryThe question doesn't tie to a specific status feel.
infoinfoInfoinfoThe action is informational — switching contexts.
successsuccessCheckCircle2successThe action commits a positive change — publish.
warningwarningAlertTrianglewarningThe action is reversible but has consequences.
errordangerAlertOctagondangerThe action is destructive / irreversible.

The variant axis drives three things at once: the leading-icon halo tint, the icon glyph itself (override via icon), and the confirm button's color. Pick by what the action is, not by what the dialog should look like — the visual treatment falls out automatically.

Variant shortcuts

Loading preview…
VariantShortcuts.tsx

Async flows

The canonical pattern: await the confirm, bail out on false, run the real work after.

Guard a destructive action

Loading preview…
AsyncFlow.tsx

For high-stakes confirms (account deletion, billing changes), set closeOnBackdropClick: false so an accidental click outside the dialog doesn't read as a cancel — the user has to take an explicit choice on one of the two buttons or press Esc.

Dropping the icon

Some prompts read better without a leading status icon — a plain "do you want to continue?" question, or a confirm that's already next to a strong visual context (e.g. a delete button on a card that's already highlighted). Pass showIcon: false:

Without the leading icon

Loading preview…
WithoutIcon.tsx

Accessibility

  • The dialog renders as role="alertdialog" (not dialog) because confirms always require a response. Screen readers announce the title + description on open without waiting for the user to navigate to them.
  • aria-labelledby / aria-describedby are wired automatically when title / description are present.
  • Focus moves to the confirm button on open (the desktop convention — Enter activates the primary action) and returns to the trigger on close.
  • A focus trap keeps Tab / Shift+Tab inside the dialog.
  • Esc resolves the promise with false (unless closeOnEscape: false).
  • Body scroll is locked while the dialog is open via the engine's reference-counted useScrollLock, so a Confirm-over-Modal combo collapses to a single lock + unlock pair.

Props

The ConfirmDisplayOptions shape — every field is optional.

PropTypeDefaultPurpose
variantConfirmVariant'default'Drives icon, header tint, and confirm-button color.
showIconbooleantrueWhether to render the leading icon halo.
iconReactNodeper variantOverride the auto-picked variant icon.
titleReactNode—Headline rendered as h2 and wired to aria-labelledby.
descriptionReactNode—Supporting copy wired to aria-describedby.
confirmTextReactNode'Confirm'Label on the confirm (primary) button.
cancelTextReactNode'Cancel'Label on the cancel (secondary) button.
closeOnEscapebooleantrueEsc resolves the promise with false.
closeOnBackdropClickbooleantrueClicking the backdrop resolves the promise with false.
sx / style / classNametheme escape hatches—Applied to the dialog surface.

Related

  • Modal — declarative, slot-based dialog when you need a custom form / body / footer.
  • SplashScreen — full-screen first-paint surface with the same imperative pattern.
  • Toast — non-blocking feedback (use this for confirmations that don't need a yes/no).

Props

No documented props found. Add JSDoc to the component's prop interface to populate this table.