suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
GitHubGet started
DocsComponentsPatternsInteractionsChartsBlocks
Components82
  • Overview
  • Accordion
  • Alert
  • Alert Dialog
  • Alert Sheet
  • Amount Input
  • Aspect Ratio
  • Attachment
  • Avatar
  • Badge
  • Bottom Bar
  • Breadcrumb
  • Bubble
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Chip
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • FAB
  • Field
  • Fullscreen Menu
  • Haptics
  • Header
  • Hover Card
  • Icon
  • Input
  • Input Accessory Bar
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Keyboard Scroll View
  • Keypad
  • Label
  • Marker
  • Menubar
  • Message
  • Message Scroller
  • Native Select
  • Navigation Menu
  • Page Indicator
  • Pagination
  • Pin Pad
  • Popover
  • Progress
  • QR Scanner
  • Questionnaire
  • Radio Group
  • Resizable
  • Screen
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Signature Pad
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Tab Bar
  • Table
  • Tabs
  • Text
  • Textarea
  • Toast
  • Toggle
  • Toggle Group
  • Tooltip
  • Wheel Picker
  • Widget

suiss/ui · Component

Alert Dialog

A modal dialog that interrupts the user to confirm an important action or acknowledge information.

Alert Dialog is an open-source component from suiss/ui for React, React Native, SwiftUI and Jetpack Compose. The @esuiss/ui CLI copies its source into your project, so you own the code.

Alert Dialog wraps the alert dialog primitive of the chosen base. An AlertDialog root contains an AlertDialogTrigger and an AlertDialogContent. The content renders its own portal and scrim overlay, and typically holds an AlertDialogHeader (with optional AlertDialogMedia, plus AlertDialogTitle and AlertDialogDescription) followed by an AlertDialogFooter with AlertDialogCancel and AlertDialogAction buttons.

Use it when a decision must be made before continuing, such as signing out or deleting data. Unlike a regular dialog, it is not dismissed by clicking outside. For a choice of actions anchored to the bottom of the screen, use Alert Sheet.

Installation

CLI

$ pnpm dlx shadcn@latest add @esuiss/alert-dialog

Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add alert-dialog

Manual

  1. Install the dependencies and add the cn helper:
$ pnpm add @base-ui/react
  1. Add the pieces it builds on: Button, use-scrollbar-gap.

  2. Copy the source into components/ui/alert-dialog.tsx:

  1. Update the import paths to match your project.

Usage

import { AlertDialog, AlertDialogTrigger, AlertDialogPortal, AlertDialogOverlay, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogMedia, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel } 
<















<















<













AlertDialogContent already includes AlertDialogPortal and AlertDialogOverlay, so you rarely use those parts directly.

AlertDialogAction and AlertDialogCancel both close the dialog and render a Button; pass variant and size to style them. Cancel defaults to the secondary variant and Action to default. Use variant="destructive" on the action for irreversible operations.

The dialog is uncontrolled by default; use defaultOpen, or control it with open and onOpenChange.

Examples

Small

A narrower dialog using size="sm" with a destructive action.

With media

Adds an icon above the title with AlertDialogMedia.

API Reference

Anatomy10 parts
<AlertDialog>
<AlertDialogTrigger />
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle />
<AlertDialogDescription />
<AlertDialogMedia />
01

AlertDialog

The root that manages the open state.

Also accepts the props of the primitive part it wraps.

02

AlertDialogTrigger

The control that opens the dialog.

Also accepts the props of the primitive part it wraps.

03

AlertDialogPortal

Renders the dialog into a portal outside the DOM hierarchy.

Also accepts the props of the primitive part it wraps.

04

AlertDialogOverlay

The full-screen scrim behind the dialog.

Also accepts the props of the primitive part it wraps.

05

AlertDialogContent

The dialog surface, rendered with its own portal and overlay.

PropTypeDefaultDescription
size"default" | "sm""default"Maximum width of the dialog. sm is narrower than default.

Also accepts the props of the primitive part it wraps.

06

AlertDialogHeader

A centered layout wrapper for the media, title and description.

Also accepts the props of React.ComponentProps<"div">.

07

AlertDialogFooter

A two-column grid for the cancel and action buttons.

Also accepts the props of React.ComponentProps<"div">.

08

AlertDialogMedia

A circular container for an icon or image above the title.

Also accepts the props of React.ComponentProps<"div">.

09

AlertDialogTitle

The heading that names the dialog.

Also accepts the props of React.ComponentProps<typeof AlertDialogPrimitive.Title>.

10

AlertDialogDescription

Supporting text that describes the dialog.

Also accepts the props of React.ComponentProps<typeof AlertDialogPrimitive.Description>.

11

AlertDialogAction

A button that confirms and closes the dialog.

Also accepts the props of the primitive part it wraps, plus Button.

12

AlertDialogCancel

A button that dismisses the dialog without acting.

Also accepts the props of the primitive part it wraps, plus Button.

Accessibility

  • The popup has the alertdialog role, named by AlertDialogTitle and described by AlertDialogDescription.
  • Opening moves focus into the dialog; closing returns focus to the trigger.
  • Escape closes the dialog.
  • Clicking the overlay does not close it; a choice must be made with a footer button or Escape.

Notes

  • Styling hooks: data-slot values from alert-dialog through alert-dialog-cancel; the content also sets data-size.
  • Surface tokens: bg-popover for the dialog, bg-black/50 for the overlay and bg-muted for media.
  • Enter and exit use fade and zoom animations keyed to data-open and data-closed.

AlertDialog is a modal that cannot be dismissed by tapping the scrim. AlertDialogContent renders through a portal (inside a full-window overlay on iOS) with a dimmed scrim and a centered card that scales and fades in.

Use it for confirmations that need an explicit choice, such as deleting data. For less critical content that can be dismissed freely, use Dialog; for a list of choices anchored to the bottom of the screen, use Alert Sheet.

Installation

$ pnpm dlx shadcn@latest add @esuiss-rn/alert-dialog

See Installation for the components.json setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add alert-dialog
$ pnpm dlx @esuiss/ui@latest add alert-dialog --platform swift
NextAlert SheetPreviousAlert

On this page

  1. 01Installation
    • CLI
    • Manual
  2. 02Usage
  3. 03Examples
    • Small
    • With media
  4. 04API Reference
    • AlertDialog
    • AlertDialogTrigger
    • AlertDialogPortal
    • AlertDialogOverlay
    • AlertDialogContent
    • AlertDialogHeader
    • AlertDialogFooter
    • AlertDialogMedia
    • AlertDialogTitle
    • AlertDialogDescription
    • AlertDialogAction
    • AlertDialogCancel
  5. 05Accessibility
  6. 06Notes
  7. 07Installation
    • Manual
  8. 08Usage
  9. 09Examples
    • Default
    • Small
    • With media
    • Destructive
  10. 10API Reference
    • AlertDialog
    • AlertDialogOverlay
    • AlertDialogContent
    • AlertDialogHeader
    • AlertDialogFooter
    • AlertDialogMedia
    • AlertDialogTitle
    • AlertDialogDescription
    • AlertDialogAction
    • AlertDialogCancel
  11. 11Accessibility
  12. 12Notes
from
"@/components/ui/alert-dialog"
AlertDialog
>
<AlertDialogTrigger render={<Button variant="outline" />}>
Sign out
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Sign out of your account?</AlertDialogTitle>
<AlertDialogDescription>
You will need to sign in again to access your projects and settings.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Sign out</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
AlertDialog
>
<AlertDialogTrigger asChild>
<Button variant="outline">Sign out</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Sign out of your account?</AlertDialogTitle>
<AlertDialogDescription>
You will need to sign in again to access your projects and settings.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Sign out</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
AlertDialog
>
<Button variant="outline">Sign out</Button>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Sign out of your account?</AlertDialogTitle>
<AlertDialogDescription>
You will need to sign in again to access your projects and settings.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Sign out</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel />
<AlertDialogAction />
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
$ pnpm dlx @esuiss/ui@latest add alert-dialog --platform kotlin

The CLI also adds what it builds on: Button, Icon, Text. On React Native it also adds the animated view components/ui/animated-view.tsx.

Manual

  1. Install the dependencies:
npx expo install cn react-native-reanimated react-native-screens react-native-worklets
  1. Add the pieces it builds on: Button, Icon, Text, and the primitives components/primitives/alert-dialog.tsx, components/primitives/use-render.tsx, and the animated view components/ui/animated-view.tsx from the next step.

  2. Copy the animated view into components/ui/animated-view.tsx:

uniwind/components/ui/animated-view.tsx
import { Pressable } from "react-native"
import Animated from "react-native-reanimated"

const AnimatedPressable = Animated.createAnimatedComponent(Pressable)

type AnimatedViewProps =
  | (React.ComponentProps<typeof Animated.View> &
      React.RefAttributes<React.ComponentRef<typeof Animated.View>> & {
        as?: "View"
      })
  | (React.ComponentProps<typeof AnimatedPressable> &
      React.RefAttributes<React.ComponentRef<typeof AnimatedPressable>> & {
        as: "Pressable"
      })

function AnimatedView(props: AnimatedViewProps) {
  1. Copy the source into components/ui/alert-dialog.tsx:
uniwind/components/ui/alert-dialog.tsx
import { cn } from "cn"
import * as React from "react"
import { Platform, View, type ViewProps } from "react-native"
import {
  FadeIn,
  FadeOut,
  Keyframe,
  ReduceMotion,
} from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import * as AlertDialogPrimitive from "@/components/primitives/alert-dialog"
import { resolveClassName } from "@/components/primitives/use-render"
import { AnimatedView } from "@/components/ui/animated-view"
import { Button } from "@/components/ui/button"
import { IconClassContext } from "@/components/ui/icon"
import { TextClassContext } from "@/components/ui/text"
  1. Update the import paths to match your project.
  1. Install the dependencies:
npx expo install cn react-native-reanimated react-native-screens react-native-worklets
  1. Add the pieces it builds on: Button, Icon, Text, and the primitives components/primitives/alert-dialog.tsx, components/primitives/use-render.tsx, and the animated view components/ui/animated-view.tsx from the next step.

  2. Copy the animated view into components/ui/animated-view.tsx:

  1. Copy the source into components/ui/alert-dialog.tsx:
  1. Update the import paths to match your project.
  1. Install the dependencies:
npx expo install react-native-reanimated react-native-screens react-native-worklets
  1. Add the pieces it builds on: Button, Icon, Text, and the primitives components/primitives/alert-dialog.tsx, components/primitives/use-render.tsx, and the animated view components/ui/animated-view.tsx from the next step.

  2. Copy the animated view into components/ui/animated-view.tsx:

  1. Copy the source into components/ui/alert-dialog.tsx:
  1. Update the import paths to match your project.
  1. Target iOS 17 or later.

  2. Add the Swift package to your app:

Package.swift
.package(url: "https://github.com/phosphor-icons/swift", from: "2.1.0")
  1. Copy the theme files every view uses:
swift/Theme/Theme.swift
import SwiftUI
import UIKit

struct RGBA: Sendable {
  let red: CGFloat
  let green: CGFloat
  let blue: CGFloat
  let alpha: CGFloat

  init(_ hex: UInt32, _ alpha: CGFloat = 1) {
    red = CGFloat((hex >> 16) & 0xFF) / 255
    green = CGFloat((hex >> 8) & 0xFF) / 255
    blue = CGFloat(hex & 0xFF) / 255
    self.alpha = alpha
  }
swift/Theme/Tokens.swift
import SwiftUI

public enum EsuissColor {
  public static let background = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x000000))
  public static let foreground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let card = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F))
  public static let cardForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let popover = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F))
  public static let popoverForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let primary = Color.adaptive(light: .init(0x0071E3), dark: .init(0x2997FF))
  public static let primaryForeground = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0xFFFFFF))
  public static let secondary = Color.adaptive(light: .init(0x000000, 0.04), dark: .init(0xFFFFFF, 0.1))
  public static let secondaryForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let muted = Color.adaptive(light: .init(0xF5F5F7), dark: .init(0x161617))
  public static let mutedForeground = Color.adaptive(light: .init(0x6E6E73), dark: .init(0xA1A1A6))
  public static let accent = Color.adaptive(light: .init(0x000000, 0.06), dark: .init(0xFFFFFF, 0.1))
  1. Copy the source and every file it uses:
swift/Components/AlertDialog.swift
import SwiftUI

extension View {
  public func alertDialog<Content: View>(
    isPresented: Binding<Bool>,
    @ViewBuilder content: @escaping () -> Content
  ) -> some View {
    modifier(
      CoverPresenter(isPresented: isPresented) { visible in
        CenteredStage(visible: visible, dismissible: false, content: content)
      }
    )
  }
}

public struct AlertDialogContent<Content: View>: View {
swift/Components/Button.swift
import SwiftUI

public struct VariantButtonStyle: ButtonStyle {
  public enum Variant: CaseIterable, Sendable {
    case `default`, outline, secondary, tinted, ghost, plain, destructive, link
  }

  public enum Size: CaseIterable, Sendable {
    case `default`, xs, sm, lg, xl, icon, iconXs, iconSm, iconLg, iconXl

    var height: CGFloat {
      switch self {
      case .xs, .iconXs: 24
      case .sm, .iconSm: 32
      case .default, .icon: 36
      case .lg, .iconLg: 44
swift/Components/ButtonGroup.swift
import SwiftUI

extension EnvironmentValues {
  @Entry var buttonGroup: Axis? = nil
  @Entry var buttonGroupSize: CGSize = .zero
}

enum ButtonGroupSpace: Hashable {
  case group
}

struct ButtonGroupFrame {
  let bounds: Anchor<CGRect>
  let outlined: Bool
}
swift/Components/Dialog.swift
import SwiftUI

extension View {
  public func dialog<Content: View>(
    isPresented: Binding<Bool>,
    @ViewBuilder content: @escaping () -> Content
  ) -> some View {
    modifier(
      CoverPresenter(isPresented: isPresented) { visible in
        CenteredStage(visible: visible, dismissible: true, content: content)
      }
    )
  }
}

struct CenteredStage<Content: View>: View {
swift/Components/Overlay.swift
import PhosphorSwift
import Observation
import SwiftUI

public enum PopupAlignment: Sendable, CaseIterable {
  case start, center, end
}

struct OverlayDismiss: Sendable {
  let action: @MainActor @Sendable () -> Void

  @MainActor func callAsFunction() {
    action()
  }
}
swift/Components/TextVariant.swift
import SwiftUI

public enum TextVariant: CaseIterable, Sendable {
  case `default`, h1, h2, h3, h4, p, blockquote, code, lead, large, small, muted

  var style: TextStyle {
    switch self {
    case .h1: .xl3
    case .h2: .xl2
    case .h3: .xl
    case .h4, .lead, .large: .lg
    case .code, .small, .muted: .sm
    default: .base
    }
  }
  1. Add the files to your app target.
  1. Set minSdk to 24 or higher and add the dependencies:
build.gradle.kts
implementation(platform("androidx.compose:compose-bom:2026.09.00"))
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.ui:ui")
implementation("com.adamglin:phosphor-icon-android:1.0.0")
  1. Copy the theme files every view uses:
kotlin/theme/Theme.kt
package com.esuiss.ui.theme

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.dropShadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.shadow.Shadow as DropShadow
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
kotlin/theme/Tokens.kt
package com.esuiss.ui.theme

import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Immutable
data class Colors(
    val background: Color,
    val foreground: Color,
    val card: Color,
    val cardForeground: Color,
    val popover: Color,
    val popoverForeground: Color,
    val primary: Color,
    val primaryForeground: Color,
  1. Copy the source and every file it uses:
kotlin/components/AlertDialog.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
kotlin/components/Button.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
kotlin/components/ButtonGroup.kt
package com.esuiss.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
kotlin/components/Content.kt
package com.esuiss.ui.components

import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

val LocalContentStyle = compositionLocalOf { TextStyle.Default }

val LocalContentColor = compositionLocalOf { Color.Unspecified }

val LocalIconSize = compositionLocalOf { 20.dp }
kotlin/components/Dialog.kt
package com.esuiss.ui.components

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
kotlin/components/Icon.kt
package com.esuiss.ui.components

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import com.esuiss.ui.theme.EsuissTheme

@Composable
fun Icon(
    imageVector: ImageVector,
kotlin/components/Overlay.kt
package com.esuiss.ui.components

import android.os.Build
import android.view.Window
import android.view.WindowManager
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.EnterExitState
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideIn
  1. Update the com.esuiss.ui package names to match your project.

Usage

import { AlertDialogOverlay, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogMedia, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel } from "@/components/ui/alert-dialog"
<View className="items-start">
  <AlertDialog defaultOpen>
    <AlertDialogTrigger render={<Button variant="outline" />}>
      <Text>Show dialog</Text>
    </AlertDialogTrigger>
    <AlertDialogBody />
  </AlertDialog>
</View>

Compose AlertDialogTrigger, then AlertDialogContent with an AlertDialogHeader (AlertDialogTitle, AlertDialogDescription) and an AlertDialogFooter holding AlertDialogCancel and AlertDialogAction. Both footer buttons close the dialog and stretch to equal widths inside the footer.

AlertDialogAction and AlertDialogCancel accept Button's variant and size; use variant="destructive" for destructive confirmations. Add AlertDialogMedia above the title for an icon badge.

Use open and onOpenChange for controlled state, or defaultOpen for uncontrolled. Pass portalHost to render into a named portal host.

Examples

Default

iOS
Alert Dialog on iOS in light modeAlert Dialog on iOS in dark mode
import { View } from "react-native"
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import { Text } from "@/components/ui/text"

function AlertDialogBody() {
  return (
    <AlertDialogContent>
      <AlertDialogHeader>
        <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
        <AlertDialogDescription>
          This action cannot be undone. This will permanently delete your
          account and remove your data from our servers.
        </AlertDialogDescription>
      </AlertDialogHeader>
      <AlertDialogFooter>
        <AlertDialogCancel>
          <Text>Cancel</Text>
        </AlertDialogCancel>
        <AlertDialogAction>
          <Text>Continue</Text>
        </AlertDialogAction>
      </AlertDialogFooter>
    </AlertDialogContent>
  )
}

export function AlertDialogExample() {
  return (
    <View className="items-start">
      <AlertDialog>
        <AlertDialogTrigger render={<Button variant="outline" />}>
          <Text>Show dialog</Text>
        </AlertDialogTrigger>
        <AlertDialogBody />
      </AlertDialog>
    </View>
  )
}
import EsuissUI
import SwiftUI

struct AlertDialogExample: View {
  var body: some View {
    AlertDialogDemo(title: "Show dialog", variant: .outline) {
      AlertDialogBody()
    }
  }
}

struct AlertDialogDemo<Body: View>: View {
  let title: LocalizedStringKey
  let variant: VariantButtonStyle.Variant
  let content: () -> Body

  @State private var open: Bool

  init(
    title: LocalizedStringKey,
    variant: VariantButtonStyle.Variant,
    open: Bool = false,
    @ViewBuilder content: @escaping () -> Body
  ) {
    self.title = title
    self.variant = variant
    self.content = content
    _open = State(initialValue: open)
  }

  var body: some View {
    Button(title) { open = true }
      .buttonStyle(.variant(variant))
      .alertDialog(isPresented: $open, content: content)
  }
}

struct AlertDialogBody: View {
  var body: some View {
    AlertDialogContent {
      AlertDialogHeader {
        AlertDialogTitle("Are you absolutely sure?")
        AlertDialogDescription(
          "This action cannot be undone. This will permanently delete your account and remove your data from our servers."
        )
      }
      AlertDialogFooter {
        AlertDialogCancel()
        AlertDialogAction("Continue")
      }
    }
  }
}
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.esuiss.ui.components.AlertDialog
import com.esuiss.ui.components.AlertDialogAction
import com.esuiss.ui.components.AlertDialogCancel
import com.esuiss.ui.components.AlertDialogDescription
import com.esuiss.ui.components.AlertDialogFooter
import com.esuiss.ui.components.AlertDialogHeader
import com.esuiss.ui.components.AlertDialogTitle
import com.esuiss.ui.components.Button
import com.esuiss.ui.components.ButtonVariant

@Composable
fun AlertDialogExample() {
    AlertDialogDefault(defaultOpen = false)
}

@Composable
fun AlertDialogDefault(defaultOpen: Boolean) {
    var open by rememberSaveable { mutableStateOf(defaultOpen) }
    Column {
        Button("Show dialog", onClick = { open = true }, variant = ButtonVariant.Outline)
        AlertDialog(open = open, onOpenChange = { open = it }) {
            AlertDialogHeader {
                AlertDialogTitle("Are you absolutely sure?")
                AlertDialogDescription(
                    "This action cannot be undone. This will permanently delete your account and remove your data from our servers.",
                )
            }
            AlertDialogFooter {
                AlertDialogCancel("Cancel")
                AlertDialogAction("Continue", onClick = {})
            }
        }
    }
}

Small

The narrower sm content size.

iOS
Alert Dialog (small) on iOS in light modeAlert Dialog (small) on iOS in dark mode
import { View } from "react-native"
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import { Text } from "@/components/ui/text"

export function AlertDialogSmallExample() {
  return (
    <View className="items-start">
      <AlertDialog>
        <AlertDialogTrigger render={<Button variant="outline" />}>
          <Text>Discard draft</Text>
        </AlertDialogTrigger>
        <AlertDialogContent size="sm">
          <AlertDialogHeader>
            <AlertDialogTitle>Discard draft?</AlertDialogTitle>
            <AlertDialogDescription>
              Your unsaved message will be lost.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>
              <Text>Keep</Text>
            </AlertDialogCancel>
            <AlertDialogAction variant="destructive">
              <Text>Discard</Text>
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </View>
  )
}
import EsuissUI
import SwiftUI

struct AlertDialogSmallExample: View {
  var body: some View {
    AlertDialogDemo(title: "Discard draft", variant: .outline) {
      AlertDialogContent(size: .sm) {
        AlertDialogHeader {
          AlertDialogTitle("Discard draft?")
          AlertDialogDescription("Your unsaved message will be lost.")
        }
        AlertDialogFooter {
          AlertDialogCancel("Keep")
          AlertDialogAction("Discard", variant: .destructive)
        }
      }
    }
  }
}

struct AlertDialogDemo<Body: View>: View {
  let title: LocalizedStringKey
  let variant: VariantButtonStyle.Variant
  let content: () -> Body

  @State private var open: Bool

  init(
    title: LocalizedStringKey,
    variant: VariantButtonStyle.Variant,
    open: Bool = false,
    @ViewBuilder content: @escaping () -> Body
  ) {
    self.title = title
    self.variant = variant
    self.content = content
    _open = State(initialValue: open)
  }

  var body: some View {
    Button(title) { open = true }
      .buttonStyle(.variant(variant))
      .alertDialog(isPresented: $open, content: content)
  }
}
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.esuiss.ui.components.AlertDialog
import com.esuiss.ui.components.AlertDialogAction
import com.esuiss.ui.components.AlertDialogCancel
import com.esuiss.ui.components.AlertDialogDescription
import com.esuiss.ui.components.AlertDialogFooter
import com.esuiss.ui.components.AlertDialogHeader
import com.esuiss.ui.components.AlertDialogSize
import com.esuiss.ui.components.AlertDialogTitle
import com.esuiss.ui.components.Button
import com.esuiss.ui.components.ButtonVariant

@Composable
fun AlertDialogSmall() {
    var open by rememberSaveable { mutableStateOf(false) }
    Column {
        Button("Discard draft", onClick = { open = true }, variant = ButtonVariant.Outline)
        AlertDialog(open = open, onOpenChange = { open = it }, size = AlertDialogSize.Sm) {
            AlertDialogHeader {
                AlertDialogTitle("Discard draft?")
                AlertDialogDescription("Your unsaved message will be lost.")
            }
            AlertDialogFooter {
                AlertDialogCancel("Keep")
                AlertDialogAction("Discard", onClick = {}, variant = ButtonVariant.Destructive)
            }
        }
    }
}

With media

An icon badge above the title with AlertDialogMedia.

iOS
Alert Dialog (with media) on iOS in light modeAlert Dialog (with media) on iOS in dark mode
import { TrashIcon } from "phosphor-react-native"
import { View } from "react-native"
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import { Icon } from "@/components/ui/icon"
import { Text } from "@/components/ui/text"

export function AlertDialogWithMediaExample() {
  return (
    <View className="items-start">
      <AlertDialog>
        <AlertDialogTrigger render={<Button variant="destructive" />}>
          <Text>Delete project</Text>
        </AlertDialogTrigger>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogMedia className="bg-destructive/10">
              <Icon as={TrashIcon} className="text-destructive" />
            </AlertDialogMedia>
            <AlertDialogTitle>Delete this project?</AlertDialogTitle>
            <AlertDialogDescription>
              This permanently removes the project and all of its files. This
              action cannot be undone.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>
              <Text>Cancel</Text>
            </AlertDialogCancel>
            <AlertDialogAction variant="destructive">
              <Text>Delete</Text>
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </View>
  )
}
import EsuissUI
import PhosphorSwift
import SwiftUI

struct AlertDialogWithMediaExample: View {
  var body: some View {
    AlertDialogDemo(title: "Delete project", variant: .destructive) {
      AlertDialogContent {
        AlertDialogHeader {
          AlertDialogMedia {
            Ph.trash.regular
              .foregroundStyle(EsuissColor.destructive)
          }
          AlertDialogTitle("Delete this project?")
          AlertDialogDescription(
            "This permanently removes the project and all of its files. This action cannot be undone."
          )
        }
        AlertDialogFooter {
          AlertDialogCancel()
          AlertDialogAction("Delete", variant: .destructive)
        }
      }
    }
  }
}

struct AlertDialogDemo<Body: View>: View {
  let title: LocalizedStringKey
  let variant: VariantButtonStyle.Variant
  let content: () -> Body

  @State private var open: Bool

  init(
    title: LocalizedStringKey,
    variant: VariantButtonStyle.Variant,
    open: Bool = false,
    @ViewBuilder content: @escaping () -> Body
  ) {
    self.title = title
    self.variant = variant
    self.content = content
    _open = State(initialValue: open)
  }

  var body: some View {
    Button(title) { open = true }
      .buttonStyle(.variant(variant))
      .alertDialog(isPresented: $open, content: content)
  }
}
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import com.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.Trash
import com.esuiss.ui.components.AlertDialog
import com.esuiss.ui.components.AlertDialogAction
import com.esuiss.ui.components.AlertDialogCancel
import com.esuiss.ui.components.AlertDialogDescription
import com.esuiss.ui.components.AlertDialogFooter
import com.esuiss.ui.components.AlertDialogHeader
import com.esuiss.ui.components.AlertDialogMedia
import com.esuiss.ui.components.AlertDialogTitle
import com.esuiss.ui.components.Button
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.Icon
import com.esuiss.ui.theme.EsuissTheme

@Composable
fun AlertDialogWithMedia() {
    var open by rememberSaveable { mutableStateOf(false) }
    val colors = EsuissTheme.colors
    Column {
        Button("Delete project", onClick = { open = true }, variant = ButtonVariant.Destructive)
        AlertDialog(open = open, onOpenChange = { open = it }) {
            AlertDialogHeader {
                AlertDialogMedia {
                    Box(
                        Modifier
                            .matchParentSize()
                            .background(colors.popover, CircleShape)
                            .background(colors.destructive.copy(alpha = 0.1f), CircleShape),
                    )
                    Icon(PhosphorIcons.Regular.Trash, contentDescription = null, tint = colors.destructive)
                }
                AlertDialogTitle("Delete this project?")
                AlertDialogDescription(
                    "This permanently removes the project and all of its files. This action cannot be undone.",
                )
            }
            AlertDialogFooter {
                AlertDialogCancel("Cancel")
                AlertDialogAction("Delete", onClick = {}, variant = ButtonVariant.Destructive)
            }
        }
    }
}

Destructive

A destructive action button for an irreversible confirmation.

iOS
Alert Dialog (destructive) on iOS in light modeAlert Dialog (destructive) on iOS in dark mode
import { View } from "react-native"
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import { Text } from "@/components/ui/text"

export function AlertDialogDestructiveExample() {
  return (
    <View className="items-start">
      <AlertDialog>
        <AlertDialogTrigger render={<Button variant="destructive" />}>
          <Text>Delete project</Text>
        </AlertDialogTrigger>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Delete project?</AlertDialogTitle>
            <AlertDialogDescription>
              All deployments and settings for this project will be removed.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>
              <Text>Cancel</Text>
            </AlertDialogCancel>
            <AlertDialogAction variant="destructive">
              <Text>Delete</Text>
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </View>
  )
}
import EsuissUI
import SwiftUI

struct AlertDialogDestructiveExample: View {
  var body: some View {
    AlertDialogDemo(title: "Delete project", variant: .destructive) {
      AlertDialogContent {
        AlertDialogHeader {
          AlertDialogTitle("Delete project?")
          AlertDialogDescription("All deployments and settings for this project will be removed.")
        }
        AlertDialogFooter {
          AlertDialogCancel()
          AlertDialogAction("Delete", variant: .destructive)
        }
      }
    }
  }
}

struct AlertDialogDemo<Body: View>: View {
  let title: LocalizedStringKey
  let variant: VariantButtonStyle.Variant
  let content: () -> Body

  @State private var open: Bool

  init(
    title: LocalizedStringKey,
    variant: VariantButtonStyle.Variant,
    open: Bool = false,
    @ViewBuilder content: @escaping () -> Body
  ) {
    self.title = title
    self.variant = variant
    self.content = content
    _open = State(initialValue: open)
  }

  var body: some View {
    Button(title) { open = true }
      .buttonStyle(.variant(variant))
      .alertDialog(isPresented: $open, content: content)
  }
}
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.esuiss.ui.components.AlertDialog
import com.esuiss.ui.components.AlertDialogAction
import com.esuiss.ui.components.AlertDialogCancel
import com.esuiss.ui.components.AlertDialogDescription
import com.esuiss.ui.components.AlertDialogFooter
import com.esuiss.ui.components.AlertDialogHeader
import com.esuiss.ui.components.AlertDialogTitle
import com.esuiss.ui.components.Button
import com.esuiss.ui.components.ButtonVariant

@Composable
fun AlertDialogDestructive() {
    var open by rememberSaveable { mutableStateOf(false) }
    Column {
        Button("Delete project", onClick = { open = true }, variant = ButtonVariant.Destructive)
        AlertDialog(open = open, onOpenChange = { open = it }) {
            AlertDialogHeader {
                AlertDialogTitle("Delete project?")
                AlertDialogDescription("All deployments and settings for this project will be removed.")
            }
            AlertDialogFooter {
                AlertDialogCancel("Cancel")
                AlertDialogAction("Delete", onClick = {}, variant = ButtonVariant.Destructive)
            }
        }
    }
}

API Reference

01

AlertDialog

The root that manages the open state.

PropTypeDefaultDescription
openboolean-Whether the dialog is open, for controlled use.
defaultOpenbooleanfalseWhether the dialog starts open when uncontrolled.
onOpenChange(open: boolean, details) => void-Called when the dialog opens or closes.
02

AlertDialogOverlay

Dimmed scrim behind the dialog.

Also accepts the props of React.ComponentProps<typeof AlertDialogPrimitive.Backdrop>.

03

AlertDialogContent

Centered card with scale and fade transitions.

PropTypeDefaultDescription
portalHoststring-Name of the portal host to render into.
size"default" | "sm""default"default or sm maximum width.

Also accepts the props of React.ComponentProps<typeof AlertDialogPrimitive.Popup>.

04

AlertDialogHeader

Centered column for the title and description.

Also accepts the props of ViewProps.

05

AlertDialogFooter

Row of actions with equal-width buttons.

Also accepts the props of ViewProps.

06

AlertDialogMedia

Round badge for an icon above the title.

Also accepts the props of ViewProps.

07

AlertDialogTitle

Dialog heading.

Also accepts the props of React.ComponentProps<typeof AlertDialogPrimitive.Title>.

08

AlertDialogDescription

Supporting text below the title.

Also accepts the props of React.ComponentProps<typeof AlertDialogPrimitive.Description>.

09

AlertDialogAction

Button that confirms and closes the dialog.

Also accepts the props of React.ComponentProps<typeof AlertDialogPrimitive.Close>, React.ComponentProps<typeof Button>.

10

AlertDialogCancel

Secondary button that closes the dialog.

Also accepts the props of React.ComponentProps<typeof AlertDialogPrimitive.Close>, React.ComponentProps<typeof Button>.

Accessibility

  • The popup has the alertdialog role, is labelled by the title and is marked as modal for VoiceOver.
  • Focus moves to the title when the dialog opens.
  • Tapping the scrim does not close it; the Android back button and the VoiceOver escape gesture do.
  • The title has the heading role, and the action buttons have the button role.

Notes

  • On iOS the content is wrapped in a full-window overlay so it appears above native navigation.
  • Enter and exit animations respect the system Reduce Motion setting.
  • Uses bg-popover, bg-black/50, text-foreground and text-muted-foreground tokens; light and dark follow the system appearance.
iOS
Alert Dialog (open by default) on iOS in light modeAlert Dialog (open by default) on iOS in dark mode
import { View } from "react-native"
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import { Text } from "@/components/ui/text"

function AlertDialogBody() {
  return (
    <AlertDialogContent>
      <AlertDialogHeader>
        <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
        <AlertDialogDescription>
          This action cannot be undone. This will permanently delete your
          account and remove your data from our servers.
        </AlertDialogDescription>
      </AlertDialogHeader>
      <AlertDialogFooter>
        <AlertDialogCancel>
          <Text>Cancel</Text>
        </AlertDialogCancel>
        <AlertDialogAction>
          <Text>Continue</Text>
        </AlertDialogAction>
      </AlertDialogFooter>
    </AlertDialogContent>
  )
}

export function AlertDialogOpenByDefaultExample() {
  return (
    <View className="items-start">
      <AlertDialog defaultOpen>
        <AlertDialogTrigger render={<Button variant="outline" />}>
          <Text>Show dialog</Text>
        </AlertDialogTrigger>
        <AlertDialogBody />
      </AlertDialog>
    </View>
  )
}
import EsuissUI
import SwiftUI

struct AlertDialogOpenByDefaultExample: View {
  var body: some View {
    AlertDialogDemo(title: "Show dialog", variant: .outline, open: true) {
      AlertDialogBody()
    }
  }
}

struct AlertDialogDemo<Body: View>: View {
  let title: LocalizedStringKey
  let variant: VariantButtonStyle.Variant
  let content: () -> Body

  @State private var open: Bool

  init(
    title: LocalizedStringKey,
    variant: VariantButtonStyle.Variant,
    open: Bool = false,
    @ViewBuilder content: @escaping () -> Body
  ) {
    self.title = title
    self.variant = variant
    self.content = content
    _open = State(initialValue: open)
  }

  var body: some View {
    Button(title) { open = true }
      .buttonStyle(.variant(variant))
      .alertDialog(isPresented: $open, content: content)
  }
}

struct AlertDialogBody: View {
  var body: some View {
    AlertDialogContent {
      AlertDialogHeader {
        AlertDialogTitle("Are you absolutely sure?")
        AlertDialogDescription(
          "This action cannot be undone. This will permanently delete your account and remove your data from our servers."
        )
      }
      AlertDialogFooter {
        AlertDialogCancel()
        AlertDialogAction("Continue")
      }
    }
  }
}
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.esuiss.ui.components.AlertDialog
import com.esuiss.ui.components.AlertDialogAction
import com.esuiss.ui.components.AlertDialogCancel
import com.esuiss.ui.components.AlertDialogDescription
import com.esuiss.ui.components.AlertDialogFooter
import com.esuiss.ui.components.AlertDialogHeader
import com.esuiss.ui.components.AlertDialogTitle
import com.esuiss.ui.components.Button
import com.esuiss.ui.components.ButtonVariant

@Composable
fun AlertDialogOpenByDefaultExample() {
    AlertDialogDefault(defaultOpen = true)
}

@Composable
fun AlertDialogDefault(defaultOpen: Boolean) {
    var open by rememberSaveable { mutableStateOf(defaultOpen) }
    Column {
        Button("Show dialog", onClick = { open = true }, variant = ButtonVariant.Outline)
        AlertDialog(open = open, onOpenChange = { open = it }) {
            AlertDialogHeader {
                AlertDialogTitle("Are you absolutely sure?")
                AlertDialogDescription(
                    "This action cannot be undone. This will permanently delete your account and remove your data from our servers.",
                )
            }
            AlertDialogFooter {
                AlertDialogCancel("Cancel")
                AlertDialogAction("Continue", onClick = {})
            }
        }
    }
}
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"

export default function AlertDialogDemo() {
  return (
    <AlertDialog>
      <AlertDialogTrigger render={<Button variant="outline" />}>
        Sign out
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>Sign out of your account?</AlertDialogTitle>
          <AlertDialogDescription>
            You will need to sign in again to access your projects and settings.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>Cancel</AlertDialogCancel>
          <AlertDialogAction>Sign out</AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  )
}
components/ui/alert-dialog.tsx
"use client"

import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
import type * as React from "react"
import { Button } from "@/components/ui/button"
import { useScrollbarGap } from "@/hooks/use-scrollbar-gap"
import { cn } from "@/lib/utils"

function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
  return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
}

function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
  return (
    <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
  )
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"

export default function AlertDialogSmall() {
  return (
    <AlertDialog>
      <AlertDialogTrigger render={<Button variant="outline" />}>
        Discard draft
      </AlertDialogTrigger>
      <AlertDialogContent size="sm">
        <AlertDialogHeader>
          <AlertDialogTitle>Discard draft?</AlertDialogTitle>
          <AlertDialogDescription>
            Your unsaved message will be lost.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>Keep</AlertDialogCancel>
          <AlertDialogAction variant="destructive">Discard</AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  )
}
import { TrashIcon } from "@phosphor-icons/react"
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogMedia,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"

export default function AlertDialogWithMedia() {
  return (
    <AlertDialog>
      <AlertDialogTrigger render={<Button variant="destructive" />}>
        Delete project
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogMedia className="bg-destructive/10 text-destructive">
            <TrashIcon />
          </AlertDialogMedia>
          <AlertDialogTitle>Delete this project?</AlertDialogTitle>
          <AlertDialogDescription>
            This permanently removes the project and all of its files. This
            action cannot be undone.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>Cancel</AlertDialogCancel>
          <AlertDialogAction variant="destructive">Delete</AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  )
}
nativewind/components/ui/animated-view.tsx
import { Pressable } from "react-native"
import Animated from "react-native-reanimated"

const AnimatedPressable = Animated.createAnimatedComponent(Pressable)

type AnimatedViewProps =
  | (React.ComponentProps<typeof Animated.View> &
      React.RefAttributes<React.ComponentRef<typeof Animated.View>> & {
        as?: "View"
      })
  | (React.ComponentProps<typeof AnimatedPressable> &
      React.RefAttributes<React.ComponentRef<typeof AnimatedPressable>> & {
        as: "Pressable"
      })

function AnimatedView(props: AnimatedViewProps) {
nativewind/components/ui/alert-dialog.tsx
import { cn } from "cn"
import * as React from "react"
import { Platform, View, type ViewProps } from "react-native"
import {
  FadeIn,
  FadeOut,
  Keyframe,
  ReduceMotion,
} from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import * as AlertDialogPrimitive from "@/components/primitives/alert-dialog"
import { resolveClassName } from "@/components/primitives/use-render"
import { AnimatedView } from "@/components/ui/animated-view"
import { Button } from "@/components/ui/button"
import { IconClassContext } from "@/components/ui/icon"
import { TextClassContext } from "@/components/ui/text"
stylesheet/components/ui/animated-view.tsx
import { Pressable } from "react-native"
import Animated from "react-native-reanimated"

const AnimatedPressable = Animated.createAnimatedComponent(Pressable)

type AnimatedViewProps =
  | (React.ComponentProps<typeof Animated.View> &
      React.RefAttributes<React.ComponentRef<typeof Animated.View>> & {
        as?: "View"
      })
  | (React.ComponentProps<typeof AnimatedPressable> &
      React.RefAttributes<React.ComponentRef<typeof AnimatedPressable>> & {
        as: "Pressable"
      })

function AnimatedView(props: AnimatedViewProps) {
stylesheet/components/ui/alert-dialog.tsx
import * as React from "react"
import { Platform, StyleSheet, View, type ViewProps } from "react-native"
import {
  FadeIn,
  FadeOut,
  Keyframe,
  ReduceMotion,
} from "react-native-reanimated"
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"
import * as AlertDialogPrimitive from "@/components/primitives/alert-dialog"
import { AnimatedView } from "@/components/ui/animated-view"
import { Button } from "@/components/ui/button"
import { TextStyleContext } from "@/components/ui/text"
import { fontSize, radius, shadow, useTheme } from "@/components/ui/theme"

const AlertDialog = AlertDialogPrimitive.Root