diff --git a/assets/translations/en.json b/assets/translations/en.json
index aae381b..40bea63 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -211,6 +211,9 @@
     "other": "{} results"
   },
   "postSearchTook": "Took {}",
+  "postDelete": "Delete post {}",
+  "postDeleteDescription": "Are you sure you want to delete this post? This operation is irreversible.",
+  "postDeleted": "Post {} has been deleted.",
   "call" : "Call",
   "callOngoingNotice": "A call is ongoing",
   "callJoin": "Join",
diff --git a/assets/translations/zh.json b/assets/translations/zh.json
index 1c91e49..59b9d20 100644
--- a/assets/translations/zh.json
+++ b/assets/translations/zh.json
@@ -211,6 +211,9 @@
     "other": "搜索到 {} 个结果"
   },
   "postSearchTook": "耗时 {}",
+  "postDelete": "删除帖子 {}",
+  "postDeleteDescription": "你确定要删除这个帖子吗?该操作不可撤销。",
+  "postDeleted": "帖子 {} 已被删除。",
   "call": "通话",
   "callOngoingNotice": "一则通话进行中",
   "callJoin": "加入",
diff --git a/lib/screens/explore.dart b/lib/screens/explore.dart
index 95b41b5..9b0de23 100644
--- a/lib/screens/explore.dart
+++ b/lib/screens/explore.dart
@@ -164,6 +164,10 @@ class _ExploreScreenState extends State<ExploreScreen> {
                     onChanged: (data) {
                       setState(() => _posts[idx] = data);
                     },
+                    onDeleted: () {
+                      _posts.clear();
+                      _fetchPosts();
+                    },
                   ),
                   onTap: () {
                     GoRouter.of(context).pushNamed(
diff --git a/lib/screens/post/post_detail.dart b/lib/screens/post/post_detail.dart
index 5cf2729..d890b8d 100644
--- a/lib/screens/post/post_detail.dart
+++ b/lib/screens/post/post_detail.dart
@@ -113,6 +113,9 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
                 onChanged: (data) {
                   setState(() => _data = data);
                 },
+                onDeleted: () {
+                  Navigator.pop(context);
+                },
               ),
             ),
           const SliverToBoxAdapter(child: Divider(height: 1)),
diff --git a/lib/screens/post/post_search.dart b/lib/screens/post/post_search.dart
index 32686d7..afffde0 100644
--- a/lib/screens/post/post_search.dart
+++ b/lib/screens/post/post_search.dart
@@ -101,6 +101,10 @@ class _PostSearchScreenState extends State<PostSearchScreen> {
                   onChanged: (data) {
                     setState(() => _posts[idx] = data);
                   },
+                  onDeleted: () {
+                    _posts.clear();
+                    _fetchPosts();
+                  },
                 ),
                 onTap: () {
                   GoRouter.of(context).pushNamed(
diff --git a/lib/widgets/post/post_comment_list.dart b/lib/widgets/post/post_comment_list.dart
index da4a148..355be56 100644
--- a/lib/widgets/post/post_comment_list.dart
+++ b/lib/widgets/post/post_comment_list.dart
@@ -74,6 +74,10 @@ class PostCommentSliverListState extends State<PostCommentSliverList> {
             onChanged: (data) {
               setState(() => _posts[idx] = data);
             },
+            onDeleted: () {
+              _posts.clear();
+              _fetchPosts();
+            },
           ),
           onTap: () {
             GoRouter.of(context).pushNamed(
diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart
index 370aed4..a575e67 100644
--- a/lib/widgets/post/post_item.dart
+++ b/lib/widgets/post/post_item.dart
@@ -5,10 +5,12 @@ import 'package:material_symbols_icons/symbols.dart';
 import 'package:provider/provider.dart';
 import 'package:relative_time/relative_time.dart';
 import 'package:styled_widget/styled_widget.dart';
+import 'package:surface/providers/sn_network.dart';
 import 'package:surface/providers/userinfo.dart';
 import 'package:surface/types/post.dart';
 import 'package:surface/widgets/account/account_image.dart';
 import 'package:surface/widgets/attachment/attachment_list.dart';
+import 'package:surface/widgets/dialog.dart';
 import 'package:surface/widgets/markdown_content.dart';
 import 'package:gap/gap.dart';
 import 'package:surface/widgets/post/post_comment_list.dart';
@@ -21,6 +23,7 @@ class PostItem extends StatelessWidget {
   final bool showMenu;
   final double? maxWidth;
   final Function(SnPost data)? onChanged;
+  final Function()? onDeleted;
   const PostItem({
     super.key,
     required this.data,
@@ -29,6 +32,7 @@ class PostItem extends StatelessWidget {
     this.showMenu = true,
     this.maxWidth,
     this.onChanged,
+    this.onDeleted,
   });
 
   void _onChanged(SnPost data) {
@@ -45,8 +49,13 @@ class PostItem extends StatelessWidget {
           child: Column(
             crossAxisAlignment: CrossAxisAlignment.start,
             children: [
-              _PostContentHeader(data: data, showMenu: showMenu)
-                  .padding(horizontal: 12, vertical: 8),
+              _PostContentHeader(
+                data: data,
+                showMenu: showMenu,
+                onDeleted: () {
+                  if (onDeleted != null) onDeleted!();
+                },
+              ).padding(horizontal: 12, vertical: 8),
               if (data.body['title'] != null ||
                   data.body['description'] != null)
                 _PostHeadline(data: data).padding(horizontal: 16, bottom: 8),
@@ -217,12 +226,37 @@ class _PostContentHeader extends StatelessWidget {
   final SnPost data;
   final bool isCompact;
   final bool showMenu;
+  final Function onDeleted;
   const _PostContentHeader({
     required this.data,
     this.isCompact = false,
     this.showMenu = true,
+    required this.onDeleted,
   });
 
+  Future<void> _deletePost(BuildContext context) async {
+    final confirm = await context.showConfirmDialog(
+      'postDelete'.tr(args: ['#${data.id}']),
+      'postDeleteDescription'.tr(),
+    );
+
+    if (!confirm) return;
+    if (!context.mounted) return;
+
+    try {
+      final sn = context.read<SnNetworkProvider>();
+      await sn.client.delete('/cgi/co/posts/${data.id}', queryParameters: {
+        'publisherId': data.publisherId,
+      });
+
+      if (!context.mounted) return;
+      context.showSnackbar('postDeleted'.tr(args: ['#${data.id}']));
+    } catch (err) {
+      if (!context.mounted) return;
+      context.showErrorDialog(err);
+    }
+  }
+
   @override
   Widget build(BuildContext context) {
     final ua = context.read<UserProvider>();
@@ -302,6 +336,7 @@ class _PostContentHeader extends StatelessWidget {
                       Text('delete').tr(),
                     ],
                   ),
+                  onTap: () => _deletePost(context),
                 ),
               if (isAuthor) const PopupMenuDivider(),
               PopupMenuItem(
@@ -381,8 +416,12 @@ class _PostQuoteContent extends StatelessWidget {
       padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
       child: Column(
         children: [
-          _PostContentHeader(data: child, isCompact: true, showMenu: false)
-              .padding(bottom: 4),
+          _PostContentHeader(
+            data: child,
+            isCompact: true,
+            showMenu: false,
+            onDeleted: () {},
+          ).padding(bottom: 4),
           _PostContentBody(data: child.body),
         ],
       ),