Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Overlays

Alert Dialog

Overlays

Alert Dialog React component for Tailwind CSS v4

Workspace SwitcherContext Menu
Live props
Content
title
description
confirmLabel
cancelLabel
confirmLoadingText
errorText
hideTitle
Appearance
tone
color
size
radius
align
State
defaultOpen
confirmDisabled
Code
import { AlertDialog } from "@/components/ui/alert-dialog"

<AlertDialog />
  1. Home
  2. Components
  3. Overlays
  4. Alert Dialog
Pro

Alert Dialog React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React alert dialog for confirmations: an async confirm that loads, fails inline and shakes, plus an await confirm() API.

Press the trigger and the page dims while the card rises into the center on a smooth spring, its icon popping in a beat later. Focus lands on Cancel, the safe choice; Tab moves between the two buttons, Enter presses the focused one and Escape cancels. Clicking the dimmed page does nothing, because the question has to be answered. When the confirm returns a promise the action shows a spinner, Cancel and Escape wait, and the dialog closes once the request succeeds; if it fails the card shakes, the error slides in above the buttons and the action is ready to retry. In a type-to-confirm field Enter confirms as soon as the action is enabled. When it closes, focus goes back to the trigger, or to whatever had it before a confirm() call or a trigger-less open. On a phone it stays a centered card with the buttons stacked full width, the action on top and 44px tall. With reduced motion it only fades.

Alert Dialog is a Pro React component in the React overlay components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add alert-dialog, together with Button, Motion presets, Tones, useControllableState and UIProvider and the npm packages @radix-ui/react-alert-dialog, motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add alert-dialog

Alert Dialog is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import AlertDialog from @/components/ui/alert-dialog.

import type { ReactNode } from "react"
import { Trash2 } from "lucide-react"
import { AlertDialog, AlertDialogProvider, confirm } from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"

async function deleteListing(id: string) {
  const res = await fetch(`/api/listings/${id}`, { method: "DELETE" })
  if (!res.ok) throw new Error("Could not delete the listing. Try again.")
}

export function DeleteListing({ id }: { id: string }) {
  return (
    <AlertDialog
      trigger={
        <Button variant="soft" tone="danger" leftIcon={<Trash2 />}>
          Delete
        </Button>
      }
      tone="danger"
      title="Delete this listing?"
      description="The listing “Linus city bike” and its 12 messages will be deleted for good."
      confirmLabel="Delete"
      cancelLabel="Cancel"
      confirmLoadingText="Deleting"
      onConfirm={() => deleteListing(id)}
    />
  )
}

// the imperative api: mount the provider once, then await an answer anywhere
export function App({ children }: { children: ReactNode }) {
  return (
    <>
      {children}
      <AlertDialogProvider defaults={{ cancelLabel: "Cancel" }} />
    </>
  )
}

export async function signOutEverywhere() {
  const ok = await confirm({
    title: "Sign out of every device?",
    description: "You stay signed in on this phone only.",
    confirmLabel: "Sign out",
  })
  if (ok) console.log("signed out")
}

Props

  • titledefault ""
    ReactNode

    The question, phrased so the buttons answer it. Without one the dialog asks "Are you sure?", so a screen reader always has a title.

  • descriptiondefault ""
    ReactNode

    The consequence, in a sentence or two: what will be lost and whether it can be undone.

  • confirmLabeldefault "Continue"
    string

    The action. Name what it does ("Delete listing"), not "Yes" or "OK".

  • cancelLabeldefault "Cancel"
    string

    The safe way out. It gets the focus when the dialog opens.

  • confirmLoadingTextdefault ""
    string

    Next to the spinner while onConfirm runs, like "Deleting". The button reserves room for it, so it never changes width.

  • errorTextdefault ""
    ReactNode

    Shown above the buttons when onConfirm rejects or throws. When empty, the error's own message is used if it has one, then "Something went wrong. Try again."

  • hideTitledefault false
    boolean

    Keeps the title for screen readers only, for a dialog whose description says it all.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    Colors the action and the icon tile. danger for anything destructive (with a trash icon), warning for unsaved changes, success for a positive step; neutral is black in light mode and white in dark mode.

  • colordefault ""
    string

    Any CSS color for the action and the icon tile, replacing the tone. Text on the button turns black or white by lightness.

  • sizedefault "sm"
    "sm" | "md"

    Max width 400px (sm) or 480px (md) for longer text. On a phone it is always 16px clear of both edges.

  • radiusdefault "lg"
    "none" | "sm" | "md" | "lg" | "full"

    Corner shape of the card: 0, 12, 16 or 24px. The icon tile and the buttons follow along, and full turns them into a circle and pills.

  • aligndefault "start"
    "start" | "center"

    start reads like a desktop dialog with the buttons on the right; center stacks icon and text in the middle and splits the buttons evenly, like an iOS alert.

  • defaultOpendefault false
    boolean

    Starts open while uncontrolled. The playground reopens it on change so every setting shows.

  • confirmDisableddefault false
    boolean

    Blocks the action, e.g. until the listing's name is typed. Enter in a text field confirms once it is enabled.

  • open
    boolean

    Controlled open state; pair it with onOpenChange.

  • onOpenChange
    (open: boolean) => void

    Called when it opens or closes, whether by the trigger, Cancel, Escape or a finished confirm.

  • trigger
    ReactNode

    The element that opens it, rendered with asChild: a single element that takes a ref, usually a Button.

  • icon
    ReactNode

    Shown in a 40px tile above the title. Defaults to a trash can for danger, a triangle for warning and a check for success, and none for the rest; null hides it.

  • children
    ReactNode

    Extra content between the text and the buttons, like a type-to-confirm field. It scrolls when the screen is short.

  • onConfirm
    () => (void | Promise<unknown>)

    Runs on the action. Return a promise to keep the dialog open with the action loading: resolving closes it, rejecting (or throwing) shows the error and shakes the card. A late result after the dialog closed is ignored.

  • onCancel
    () => void

    Called on Cancel or Escape, not when the dialog closes after a confirm.

  • container
    HTMLElement | null

    Where the portal mounts. document.body by default.

  • classNames
    Partial<Record<"overlay" | "content" | "icon" | "header" | "title" | "description" | "body" | "error" | "footer" | "cancel" | "action", string>>

    Extra classes for the inner parts.

  • className
    string

    Extra classes for the card, merged last. The rest of the props (id, aria-*, onOpenAutoFocus...) go to it too.

Parts

  • confirm

    function

    Asks from anywhere, even outside React: resolves true once confirmed (after onConfirm settled) and false when cancelled. Takes every AlertDialog prop except trigger and the open state. Calls made while one is open wait their turn, and focus returns to what had it when confirm() was called. Needs <AlertDialogProvider /> mounted; without it, development warns and the browser's confirm steps in. Signature: (options: ConfirmOptions) => Promise<boolean>.

  • useConfirm

    hook

    The same confirm function, for code that prefers a hook. Signature: () => typeof confirm.

  • AlertDialogProvider

    component

    Mount once near the root, next to the toaster. Renders confirm() calls one at a time; defaults go under every call's options (translated labels, a house tone). If the last provider unmounts, calls still waiting resolve false. Props: { defaults?: ConfirmOptions }.

  • AlertDialogRoot

    component

    The parts API, for custom layouts: AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogBody, AlertDialogError, AlertDialogFooter, AlertDialogCancel and AlertDialogAction. Props: { open?; defaultOpen?; onOpenChange?; onCancel?; children }.

  • AlertDialogTrigger

    component

    Opens it. Use asChild with a Button; focus returns here when the dialog closes. Props: Radix AlertDialog.Trigger props.

  • AlertDialogContent

    component

    The overlay and the card. Takes size, tone, color, radius, align, container and classNames { overlay, content }, plus Radix Content props (onOpenAutoFocus, onCloseAutoFocus...). Props: AlertDialogContentProps.

  • AlertDialogHeader

    component

    Title and description, with an optional icon tile above them in the content's tone. Scrolls on its own when the text outgrows a short screen. Props: { icon?: ReactNode; classNames?: { icon }; children } & ComponentProps<"div">.

  • AlertDialogIcon

    component

    The 40px tile in the tone's soft color that pops in on the bouncy spring. The header renders it for you when you pass icon. Takes span props.

  • AlertDialogTitle

    component

    The question, 18px semibold. Required by screen readers; hide it with the sr-only class if the design has none. Props: Radix AlertDialog.Title props.

  • AlertDialogDescription

    component

    The consequence in muted text. Leave it out and pass aria-describedby={undefined} to the content. Props: Radix AlertDialog.Description props.

  • AlertDialogBody

    component

    Extra content between the text and the buttons (a type-to-confirm field). Scrolls when the screen is short. Takes div props.

  • AlertDialogFooter

    component

    The buttons: stacked full width below 640px with the action on top, a right-aligned row above (split evenly when align is center). Takes div props.

  • AlertDialogAction

    component

    The confirm Button in the content's tone. Runs onConfirm with the loading, error and shake behavior; call event.preventDefault() in onClick to keep it from running. Props: ButtonProps & { onConfirm?; errorText? }.

  • AlertDialogCancel

    component

    The soft Cancel Button. Focused on open, disabled while a confirm runs. Props: ButtonProps.

  • AlertDialogError

    component

    Shows the failed confirm's message with role alert; children replace it. Takes p props.

  • ConfirmOptions

    type

    What confirm() takes: the AlertDialog props without trigger, open, defaultOpen and onOpenChange.

Examples

Async delete that can fail

A promise keeps it open and loading; a rejection shakes the card, shows the message and lets you retry.

Imperative confirm()

Mount AlertDialogProvider once, then await an answer from any handler. No dialog markup at the call site.

Type to confirm

confirmDisabled until the word matches, in any case. Enter in the field confirms.

Brand tone for a paid step

Non-destructive, with a custom icon and the price in the question and on the button.

Unsaved changes

warning brings its own icon; size md gives two long labels room to sit side by side.

Queued confirms

Two confirm() calls at once show one after the other, each resolving on its own.

Centered, like iOS

align="center" with radius="full": a round icon tile and two pills that share the width.

Dependencies

@radix-ui/react-alert-dialogmotionlucide-reactbuttonmotiontonesuse-controllable-stateui-config

Files

  • components/ui/alert-dialog.tsx

FAQ

Is the React Alert Dialog component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Alert Dialog?

Run npx wingo-ui@latest add alert-dialog. It also adds the Wingo UI items it builds on (Button, Motion presets, Tones, useControllableState and UIProvider) and the npm packages @radix-ui/react-alert-dialog, motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/alert-dialog.

How does Alert Dialog behave on phones?

On a phone it stays a centered card with the buttons stacked full width, the action on top and 44px tall.

See also

DialogButtonDelete ButtonToastAlert

Used in

File managerWorkflow CanvasSchema BuilderShare DialogComment ThreadFile ListMembers tableSettings LayoutDOCX EditorFile SystemAgent Status CardConversation List

and 8 more.

More React overlay components

Context MenuDrawerFamily DrawerHover CardPopoverSheet
All 13 React overlay components

Articles using this component

  • API Key Management UI: Create, Show Once, Roll, Revoke

    Build it: screens and apps · Nov 6, 2026 · 10 min read

  • React Action Sheet: iOS-Style, Async and Responsive

    Mobile UI in React · Oct 16, 2026 · 9 min read

  • React Settings Page: Section Nav, Save Bar, Danger Zone

    Build it: screens and apps · Oct 16, 2026 · 11 min read

  • React Slide to Confirm: When It Beats a Confirm Dialog

    Mobile UI in React · Oct 13, 2026 · 8 min read

PreviousWorkspace SwitcherNext Context Menu
Activeexpires Oct 12, 2026

Linus city bike

Brooklyn · 12 messages

$1,200

Confirm waits 1.4s while the button reads “Deleting”. A failed request shakes the card and shows its error.