diff --git a/components/modal/ModalConfirm.vue b/components/modal/ModalConfirm.vue
new file mode 100644
index 00000000..b0516bb9
--- /dev/null
+++ b/components/modal/ModalConfirm.vue
@@ -0,0 +1,28 @@
+<script setup lang="ts">
+import type { ConfirmDialogChoice, ConfirmDialogLabel } from '~/types'
+
+defineProps<ConfirmDialogLabel>()
+
+const emit = defineEmits<{
+  (evt: 'choice', choice: ConfirmDialogChoice): void
+}>()
+</script>
+
+<template>
+  <div flex="~ col" gap-6>
+    <div font-bold text-lg text-center>
+      {{ title }}
+    </div>
+    <div v-if="description">
+      {{ description }}
+    </div>
+    <div flex justify-end gap-2>
+      <button btn-text @click="emit('choice', 'cancel')">
+        {{ cancel || $t('common.confirm_dialog.cancel') }}
+      </button>
+      <button btn-solid @click="emit('choice', 'confirm')">
+        {{ confirm || $t('common.confirm_dialog.confirm') }}
+      </button>
+    </div>
+  </div>
+</template>
diff --git a/components/modal/ModalContainer.vue b/components/modal/ModalContainer.vue
index 31ba7cb3..23c31c34 100644
--- a/components/modal/ModalContainer.vue
+++ b/components/modal/ModalContainer.vue
@@ -1,7 +1,9 @@
 <script setup lang="ts">
 import type { Status } from 'masto'
+import type { ConfirmDialogChoice } from '~/types'
 import {
   isCommandPanelOpen,
+  isConfirmDialogOpen,
   isEditHistoryDialogOpen,
   isMediaPreviewOpen,
   isPreviewHelpOpen,
@@ -36,6 +38,11 @@ const handlePublished = (status: Status) => {
 const handlePublishClose = () => {
   lastPublishDialogStatus.value = null
 }
+
+const handleConfirmChoice = (choice: ConfirmDialogChoice) => {
+  confirmDialogChoice.value = choice
+  isConfirmDialogOpen.value = false
+}
 </script>
 
 <template>
@@ -71,5 +78,8 @@ const handlePublishClose = () => {
     <ModalDialog v-model="isCommandPanelOpen" max-w-fit flex>
       <CommandPanel @close="closeCommandPanel()" />
     </ModalDialog>
+    <ModalDialog v-model="isConfirmDialogOpen" py-4 px-8 max-w-125>
+      <ModalConfirm v-if="confirmDialogLabel" v-bind="confirmDialogLabel" @choice="handleConfirmChoice" />
+    </ModalDialog>
   </template>
 </template>
diff --git a/components/status/StatusActionsMore.vue b/components/status/StatusActionsMore.vue
index fe183b84..be948f9e 100644
--- a/components/status/StatusActionsMore.vue
+++ b/components/status/StatusActionsMore.vue
@@ -22,6 +22,7 @@ const {
 const clipboard = useClipboard()
 const router = useRouter()
 const route = useRoute()
+const { t } = useI18n()
 
 const isAuthor = $computed(() => status.account.id === currentUser.value?.account.id)
 
@@ -60,13 +61,12 @@ const shareLink = async (status: Status) => {
 }
 
 const deleteStatus = async () => {
-  // TODO confirm to delete
-  if (process.dev) {
-    // eslint-disable-next-line no-alert
-    const result = confirm('[DEV] Are you sure you want to delete this post?')
-    if (!result)
-      return
-  }
+  if (await openConfirmDialog({
+    title: t('menu.delete_confirm.title'),
+    confirm: t('menu.delete_confirm.confirm'),
+    cancel: t('menu.delete_confirm.cancel'),
+  }) !== 'confirm')
+    return
 
   removeCachedStatus(status.id)
   await masto.statuses.remove(status.id)
diff --git a/composables/dialog.ts b/composables/dialog.ts
index b7d07a98..361a0a56 100644
--- a/composables/dialog.ts
+++ b/composables/dialog.ts
@@ -1,7 +1,10 @@
 import type { Attachment, Status, StatusEdit } from 'masto'
-import type { Draft } from '~/types'
+import type { ConfirmDialogChoice, ConfirmDialogLabel, Draft } from '~/types'
 import { STORAGE_KEY_FIRST_VISIT } from '~/constants'
 
+export const confirmDialogChoice = ref<ConfirmDialogChoice>()
+export const confirmDialogLabel = ref<ConfirmDialogLabel>()
+
 export const mediaPreviewList = ref<Attachment[]>([])
 export const mediaPreviewIndex = ref(0)
 
@@ -18,6 +21,7 @@ export const isMediaPreviewOpen = ref(false)
 export const isEditHistoryDialogOpen = ref(false)
 export const isPreviewHelpOpen = ref(isFirstVisit.value)
 export const isCommandPanelOpen = ref(false)
+export const isConfirmDialogOpen = ref(false)
 
 export const lastPublishDialogStatus = ref<Status | null>(null)
 
@@ -25,6 +29,16 @@ export function openSigninDialog() {
   isSigninDialogOpen.value = true
 }
 
+export async function openConfirmDialog(label: ConfirmDialogLabel | string): Promise<ConfirmDialogChoice> {
+  confirmDialogLabel.value = typeof label === 'string' ? { title: label } : label
+  confirmDialogChoice.value = undefined
+  isConfirmDialogOpen.value = true
+
+  await until(isConfirmDialogOpen).toBe(false)
+
+  return confirmDialogChoice.value!
+}
+
 export async function openPublishDialog(draftKey = 'dialog', draft?: Draft, overwrite = false): Promise<void> {
   dialogDraftKey.value = draftKey
 
diff --git a/locales/en-US.json b/locales/en-US.json
index edf496e5..78a9dde1 100644
--- a/locales/en-US.json
+++ b/locales/en-US.json
@@ -86,6 +86,11 @@
     "toggle_zen_mode": "Toggle zen mode"
   },
   "common": {
+    "confirm_dialog": {
+      "cancel": "No",
+      "confirm": "Yes",
+      "title": "Are you sure?"
+    },
     "end_of_list": "End of the list",
     "error": "ERROR",
     "in": "in",
@@ -123,6 +128,11 @@
     "copy_link_to_post": "Copy link to this post",
     "delete": "Delete",
     "delete_and_redraft": "Delete & re-draft",
+    "delete_confirm": {
+      "cancel": "Cancel",
+      "confirm": "Delete",
+      "title": "Are you sure you want to delete this post?"
+    },
     "direct_message_account": "Direct message {0}",
     "edit": "Edit",
     "mention_account": "Mention {0}",
diff --git a/types/index.ts b/types/index.ts
index 340b6c81..190862cf 100644
--- a/types/index.ts
+++ b/types/index.ts
@@ -65,6 +65,14 @@ export interface Draft {
 }
 export type DraftMap = Record<string, Draft>
 
+export interface ConfirmDialogLabel {
+  title: string
+  description?: string
+  confirm?: string
+  cancel?: string
+}
+export type ConfirmDialogChoice = 'confirm' | 'cancel'
+
 export interface BuildInfo {
   version: string
   commit: string