---
title: "Popconfirm"
description: "A popover-based action confirmation component."
canonical_url: "https://nuxt.mhaibaraai.cn/en/docs/components/popconfirm"
---
# Popconfirm

> A popover-based action confirmation component.

## Introduction

`MPopconfirm` is a popover-style confirmation component that displays a confirmation bubble before users perform dangerous or irreversible actions. It supports both synchronous and asynchronous confirm callbacks, and fully passes through all `UPopover` positioning parameters.

> [!NOTE]
> See: https://ui.nuxt.com/docs/components/popover
> 
> Built on Nuxt UI's Popover component

## Usage

Place the trigger element in the `default` slot. Clicking it opens the confirmation bubble:

```vue
<template>
  <MPopconfirm title="Confirm Delete?" description="This action cannot be undone.">
    <UButton label="Delete Record" color="neutral" variant="outline" icon="i-lucide-trash" />
  </MPopconfirm>
</template>
```

### `type` Type

`type` affects the default icon and confirm button color, with six semantic values:

```vue
<template>
  <MPopconfirm type="warning">
    <UButton label="Open" color="neutral" variant="soft" />
  </MPopconfirm>
</template>
```

### `icon` Icon

`icon` overrides the default icon from `type`. Pass any Iconify icon name:

```vue
<template>
  <MPopconfirm type="error" icon="i-lucide-trash-2">
    <UButton label="Dangerous Action" color="error" variant="soft" icon="i-lucide-alert-triangle" />
  </MPopconfirm>
</template>
```

### `confirmButton` Confirm Button

`confirmButton` accepts full `ButtonProps`, allowing customization of label, color, icon, and variant:

```vue
<template>
  <MPopconfirm :confirm-button='{"color":"error","label":"Confirm Delete","icon":"i-lucide-trash-2"}'>
    <UButton label="Delete Record" color="error" variant="soft" />
  </MPopconfirm>
</template>
```

### `cancelButton` Cancel Button

`cancelButton` accepts `ButtonProps` or `false`. Passing `false` hides the cancel button to force confirmation:

```vue
<template>
  <MPopconfirm :cancel-button="false">
    <UButton label="Force Execute" color="warning" variant="soft" />
  </MPopconfirm>
</template>
```

### `dismissible` Close Strategy

`dismissible` defaults to `false` (strict mode). When enabled, clicking the overlay or pressing Esc closes the bubble:

```vue
<template>
  <MPopconfirm :dismissible="false">
    <UButton label="Close Strategy" color="neutral" variant="outline" />
  </MPopconfirm>
</template>
```

### `arrow` Arrow

`arrow` controls the arrow pointing to the trigger. Enabled by default:

```vue
<template>
  <MPopconfirm arrow>
    <UButton label="Arrow Toggle" color="neutral" variant="subtle" />
  </MPopconfirm>
</template>
```

## Examples

### Async Confirmation

`:on-confirm` supports returning a `Promise`. During execution, the confirm button enters a loading state and closes automatically on success:

```vue [ComponentsPopconfirmAsyncExample.vue]
<script setup lang="ts">
const result = ref('')

async function handleSubmit() {
  await new Promise(resolve => setTimeout(resolve, 1500))
  result.value = '提交成功'
}
</script>

<template>
  <div class="flex flex-wrap items-center gap-3">
    <MPopconfirm
      type="primary"
      title="确认提交？"
      description="提交后将立即生效，请确认操作。"
      :on-confirm="handleSubmit"
    >
      <UButton color="primary" label="提交申请" icon="i-lucide-send" />
    </MPopconfirm>

    <span v-if="result" class="text-sm text-success">{{ result }}</span>
  </div>
</template>
```

### Body Slot

Use the `body` slot to insert any content. Pass an empty string to `description` to hide the default description area:

```vue [ComponentsPopconfirmSlotExample.vue]
<script setup lang="ts">
const result = ref('')
</script>

<template>
  <div class="flex flex-wrap items-center gap-3">
    <MPopconfirm
      title="删除用户"
      :description="''"
      :on-confirm="() => { result = '用户已删除' }"
    >
      <template #body>
        <div class="flex flex-col gap-2 py-1">
          <p class="text-sm text-muted">
            即将删除以下用户，操作不可撤销：
          </p>
          <div class="flex items-center gap-2 rounded-md bg-elevated px-3 py-2">
            <UAvatar size="xs" icon="i-lucide-user" />
            <div class="flex flex-col">
              <span class="text-xs font-medium text-highlighted">张三</span>
              <span class="text-xs text-muted">zhangsan@example.com</span>
            </div>
          </div>
        </div>
      </template>

      <UButton color="error" variant="soft" label="删除用户" icon="i-lucide-user-x" />
    </MPopconfirm>

    <span v-if="result" class="text-sm text-error">{{ result }}</span>
  </div>
</template>
```

### Style Customization

Override the class of internal sections via the `ui` prop, supporting `title`, `description`, `footer`, `content`, and more:

```vue [ComponentsPopconfirmUiExample.vue]
<script setup lang="ts">
const result = ref('')
</script>

<template>
  <div class="flex flex-wrap items-center gap-3">
    <MPopconfirm
      type="info"
      title="样式定制示例"
      description="通过 ui 属性覆盖内部各区块的 class，实现精细样式控制。"
      :on-confirm="() => { result = '已确认' }"
      :ui="{
        content: 'w-72',
        title: 'text-info text-base',
        description: 'text-xs text-info/70',
        footer: 'justify-between pt-2 border-t border-default'
      }"
      :confirm-button="{ label: '好的，执行', color: 'info' }"
      :cancel-button="{ label: '不了，取消', variant: 'ghost' }"
    >
      <UButton color="info" variant="soft" label="打开定制气泡" icon="i-lucide-palette" />
    </MPopconfirm>

    <span v-if="result" class="text-sm text-muted">{{ result }}</span>
  </div>
</template>
```

### Popover Direction

Pass the `content` prop to the underlying `UPopover` to support `top`, `bottom`, `left`, and `right` placement:

```vue [ComponentsPopconfirmSideExample.vue]
<script setup lang="ts">
type Side = 'top' | 'bottom' | 'left' | 'right'

const sides: Side[] = ['top', 'bottom', 'left', 'right']
const result = ref('')
</script>

<template>
  <div class="flex flex-wrap items-center gap-2">
    <MPopconfirm
      v-for="side in sides"
      :key="side"
      :content="{ side }"
      :title="`从 ${side} 方弹出`"
      description="透传 Popover 定位参数，支持 top / bottom / left / right。"
      :on-confirm="() => { result = `已从 ${side} 方确认` }"
    >
      <UButton color="neutral" variant="outline" size="sm" :label="side" />
    </MPopconfirm>

    <span v-if="result" class="ml-2 text-sm text-muted">{{ result }}</span>
  </div>
</template>
```

### Error Handling

When the `onConfirm` callback throws an exception, the popover remains open and fires the `@error` event where you can display error feedback:

```vue [ComponentsPopconfirmErrorExample.vue]
<script setup lang="ts">
const errorMsg = ref('')

const toast = useToast()

async function failingAction() {
  await new Promise(resolve => setTimeout(resolve, 800))
  throw new Error('服务器返回错误：操作被拒绝')
}

function handleError(err: unknown) {
  errorMsg.value = err instanceof Error ? err.message : '未知错误'
  toast.add({
    color: 'error',
    title: '操作失败',
    description: errorMsg.value
  })
}
</script>

<template>
  <div class="flex flex-col gap-3">
    <div class="flex flex-wrap items-center gap-3">
      <MPopconfirm
        type="error"
        title="模拟操作失败"
        description="确认后将触发一个失败的异步操作，弹层保持打开并上报错误。"
        :on-confirm="failingAction"
        @error="handleError"
      >
        <UButton color="error" variant="soft" label="触发失败操作" icon="i-lucide-zap-off" />
      </MPopconfirm>
    </div>
  </div>
</template>
```

### Event Callbacks

Synchronous confirm, cancel, async confirm, and exceptions fire `confirm`, `cancel`, and `error` in sequence:

```vue [ComponentsPopconfirmEventsExample.vue]
<script setup lang="ts">
const log = ref<string[]>([])

function record(msg: string) {
  log.value = [`[${new Date().toLocaleTimeString()}] ${msg}`, ...log.value].slice(0, 8)
}

async function asyncConfirm() {
  await new Promise(resolve => setTimeout(resolve, 1200))
  record('async confirmed')
}

function rejectedConfirm() {
  throw new Error('被拒绝')
}
</script>

<template>
  <div class="grid grid-cols-1 lg:grid-cols-[1fr_220px] gap-3">
    <div class="flex flex-wrap gap-2">
      <MPopconfirm
        type="warning"
        title="点击确认或取消"
        description="确认会触发 confirm 事件，取消会触发 cancel 事件。"
        :on-confirm="() => record('confirmed')"
        @cancel="record('cancelled')"
      >
        <UButton color="primary" variant="soft" label="同步事件" />
      </MPopconfirm>

      <MPopconfirm
        type="info"
        title="异步事件"
        description="确认按钮等待 onConfirm 异步完成后才触发 confirm 并关闭。"
        :on-confirm="asyncConfirm"
      >
        <UButton color="info" variant="soft" label="异步确认" />
      </MPopconfirm>

      <MPopconfirm
        type="error"
        title="抛错保留"
        description="onConfirm 抛出错误时弹层保持打开，并通过 error 事件暴露异常。"
        :on-confirm="rejectedConfirm"
        @error="(e: unknown) => record(`error: ${(e as Error).message}`)"
      >
        <UButton color="error" variant="outline" label="抛错保留" />
      </MPopconfirm>
    </div>

    <div class="rounded border border-default p-2 text-xs text-muted space-y-1 max-h-40 overflow-auto">
      <p v-if="!log.length">
        暂无事件记录
      </p>
      <p v-for="(item, idx) in log" :key="idx">
        {{ item }}
      </p>
    </div>
  </div>
</template>
```

## API

### Props

```ts
/**
 * Props for the MPopconfirm component
 */
interface MPopconfirmProps {
  /**
   * 确认气泡的标题文本。
   * @default '确认操作'
   */
  title?: string | undefined;
  /**
   * 标题下方的补充说明。
   * 传入空字符串时可隐藏描述区。
   * @default '请确认是否执行此操作?'
   */
  description?: string | undefined;
  /**
   * 预设的语义化颜色主题，会影响图标。
   * @default 'neutral'
   */
  type?: "error" | "primary" | "info" | "success" | "warning" | "neutral" | undefined;
  /**
   * 标题前展示的图标名称。
   * @default 'i-lucide-circle-question-mark'
   */
  icon?: any;
  /**
   * 气泡内容与触发器之间的箭头指示。
   * @default true
   */
  arrow?: boolean | undefined;
  /**
   * 透传给确认按钮的属性。
   * `loading` 状态由组件内部托管。
   */
  confirmButton?: OmitByKey<ButtonProps, "loading" | LinkPropsKeys> | undefined;
  /**
   * 透传给取消按钮的属性。
   * 传入 `false` 可完全隐藏取消按钮。
   * @default true
   */
  cancelButton?: boolean | ButtonProps | undefined;
  /**
   * 当 `false` 时，点击遮罩层或按下 `Esc` 键将不会关闭弹层。
   * @default false
   */
  dismissible?: boolean | undefined;
  ui?: Record<string, C> & { content?: SlotClass; arrow?: SlotClass; header?: SlotClass; title?: SlotClass; description?: SlotClass; body?: SlotClass; footer?: SlotClass; icon?: SlotClass; } | undefined;
  /**
   * The display mode of the popover.
   * @default 'click'
   */
  mode?: "click" | "hover" | undefined;
  /**
   * The content of the popover.
   * @default { side: 'bottom', sideOffset: 8, collisionPadding: 8 }
   */
  content?: Omit<PopoverContentProps, "as" | "asChild" | "forceMount"> & Partial<EmitsToProps<PopoverContentImplEmits>> | undefined;
  /**
   * Render the popover in a portal.
   * @default true
   */
  portal?: string | false | true | HTMLElement | undefined;
  /**
   * The reference (or anchor) element that is being referred to for positioning.
   * 
   * Accepts an element or a virtual element (anything with `getBoundingClientRect`),
   * and can be changed reactively to re-anchor the popover (e.g. for a guided tour).
   * If not provided will use the current component as anchor.
   */
  reference?: Element | VirtualElement | undefined;
  /**
   * The modality of the popover. When set to true, interaction with outside elements will be disabled and only popover content will be visible to screen readers.
   * @default false
   */
  modal?: boolean | undefined;
  /**
   * The duration from when the mouse enters the trigger until the hover card opens.
   */
  openDelay?: number | undefined;
  /**
   * The duration from when the mouse leaves the trigger or content until the hover card closes.
   */
  closeDelay?: number | undefined;
  /**
   * When `true`, tapping the trigger on touch devices toggles the hover card open/closed. By default touch interactions are ignored to match pointer hover semantics.
   */
  enableTouch?: boolean | undefined;
}
```

### Emits

```ts
/**
 * Emitted events for the MPopconfirm component
 */
interface MPopconfirmEmits {
  confirm: (payload: []) => void;
  cancel: (payload: []) => void;
  error: (payload: [error: unknown]) => void;
}
```

### Slots

```ts
/**
 * Slots for the MPopconfirm component
 */
interface MPopconfirmSlots {
  default(): any;
  header(): any;
  title(): any;
  description(): any;
  actions(): any;
  body(): any;
  footer(): any;
}
```

## Theme

<component-theme>



</component-theme>

## Changelog

See commit history for [src/runtime/components/Popconfirm.vue](https://github.com/mhaibaraai/movk-nuxt/commits/main/src/runtime/components/Popconfirm.vue).


## Sitemap

See the full [sitemap](https://nuxt.mhaibaraai.cn/sitemap.md) for all pages.
