diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationItemView.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationItemView.kt
index 6eccffeb5b046b59c5560900955eb4cfe180710f..6dbbd900320400dfe3a9a97f0a026e54201c5c90 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationItemView.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationItemView.kt
@@ -1,7 +1,11 @@
 package org.briarproject.briar.desktop.conversation
 
 import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.ContextMenuArea
+import androidx.compose.foundation.ContextMenuItem
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -33,6 +37,7 @@ import org.briarproject.briar.desktop.theme.msgStroke
 import org.briarproject.briar.desktop.theme.privateMessageDate
 import org.briarproject.briar.desktop.theme.textPrimary
 import org.briarproject.briar.desktop.utils.InternationalizationUtils
+import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 import org.briarproject.briar.desktop.utils.PreviewUtils.preview
 import org.briarproject.briar.desktop.utils.TimeUtils.getFormattedTimestamp
 import java.time.Instant
@@ -143,28 +148,39 @@ fun main() = preview {
 /**
  * Base Composable for all kind of messages in private chats.
  */
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun ConversationItemView(
     item: ConversationItem,
+    onDelete: (MessageId) -> Unit = {},
     content: @Composable () -> Unit
 ) {
-    val alignment = if (item.isIncoming) Alignment.Start else Alignment.End
+    val arrangement = if (item.isIncoming) Arrangement.Start else Arrangement.End
+    val alignment = if (item.isIncoming) Alignment.CenterStart else Alignment.CenterEnd
     val color = if (item.isIncoming) MaterialTheme.colors.msgIn else MaterialTheme.colors.msgOut
     val shape = if (item.isIncoming)
         RoundedCornerShape(topStart = 4.dp, topEnd = 16.dp, bottomStart = 16.dp, bottomEnd = 16.dp)
     else
         RoundedCornerShape(topStart = 16.dp, topEnd = 4.dp, bottomStart = 16.dp, bottomEnd = 16.dp)
 
-    Column(Modifier.fillMaxWidth()) {
-        Column(Modifier.fillMaxWidth(fraction = 0.8f).align(alignment)) {
-            Card(
-                backgroundColor = color,
-                elevation = 2.dp,
-                shape = shape,
-                border = BorderStroke(Dp.Hairline, MaterialTheme.colors.msgStroke),
-                modifier = Modifier.align(alignment).padding(8.dp),
+    Row(Modifier.fillMaxWidth(), arrangement) {
+        Box(Modifier.fillMaxWidth(0.8f), contentAlignment = alignment) {
+            ContextMenuArea(
+                items = {
+                    listOf(
+                        ContextMenuItem(i18n("delete")) { onDelete(item.id) }
+                    )
+                }
             ) {
-                content()
+                Card(
+                    backgroundColor = color,
+                    elevation = 2.dp,
+                    shape = shape,
+                    border = BorderStroke(Dp.Hairline, MaterialTheme.colors.msgStroke),
+                    modifier = Modifier.padding(8.dp),
+                ) {
+                    content()
+                }
             }
         }
     }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationMessageItemView.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationMessageItemView.kt
index b9ef25101f019b5b99ccf9c9345c3f54b1101e1d..3619b174251fa5b64689355d0c0f85303463b015 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationMessageItemView.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationMessageItemView.kt
@@ -43,9 +43,12 @@ fun main() = preview(
  * Composable for normal private text messages.
  */
 @Composable
-fun ConversationMessageItemView(m: ConversationMessageItem) {
+fun ConversationMessageItemView(
+    m: ConversationMessageItem,
+    onDelete: (MessageId) -> Unit = {},
+) {
     val textColor = if (m.isIncoming) MaterialTheme.colors.textPrimary else Color.White
-    ConversationItemView(m) {
+    ConversationItemView(m, onDelete) {
         Column(
             Modifier.padding(12.dp, 8.dp)
         ) {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItemView.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItemView.kt
index 48dff66eb9e3bca62a0a16f4d743594c354ca34f..8a79f0b1f96accd13c7ca7b0efcb85c90e675d56 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItemView.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItemView.kt
@@ -54,12 +54,15 @@ fun main() = preview(
  * Composable for private messages containing a notice.
  */
 @Composable
-fun ConversationNoticeItemView(m: ConversationNoticeItem) {
+fun ConversationNoticeItemView(
+    m: ConversationNoticeItem,
+    onDelete: (MessageId) -> Unit = {},
+) {
     val textColor = if (m.isIncoming) MaterialTheme.colors.textPrimary else Color.White
     val noticeBackground = if (m.isIncoming) MaterialTheme.colors.noticeIn else MaterialTheme.colors.noticeOut
     val noticeColor = if (m.isIncoming) MaterialTheme.colors.textSecondary else MaterialTheme.colors.privateMessageDate
     val text = m.text
-    ConversationItemView(m) {
+    ConversationItemView(m, onDelete) {
         Column(Modifier.width(IntrinsicSize.Max)) {
             if (text != null) {
                 Text(
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItemView.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItemView.kt
index bca4e4827d779bb9b7e1dab9f335d6cf7dd46ed7..865919e7a91b209c176339df9d9f97279f4f50ee 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItemView.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItemView.kt
@@ -73,12 +73,13 @@ fun ConversationRequestItemView(
     m: ConversationRequestItem,
     onResponse: (Boolean) -> Unit = {},
     onOpenRequestedShareable: () -> Unit = {},
+    onDelete: (MessageId) -> Unit = {},
 ) {
     val statusAlignment = if (m.isIncoming) Alignment.End else Alignment.Start
     val textColor = if (m.isIncoming) MaterialTheme.colors.textPrimary else Color.White
     val noticeBackground = if (m.isIncoming) MaterialTheme.colors.noticeIn else MaterialTheme.colors.noticeOut
     val noticeColor = if (m.isIncoming) MaterialTheme.colors.textSecondary else MaterialTheme.colors.privateMessageDate
-    ConversationItemView(m) {
+    ConversationItemView(m, onDelete) {
         Column(Modifier.width(IntrinsicSize.Max)) {
             Text(
                 m.text!!,
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt
index de2d26119794712fd402afd096cf4651af952835..89b4a307476aadb896897cace783f53945087827 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt
@@ -89,12 +89,13 @@ fun ConversationScreen(
                 ) {
                     items(viewModel.messages) { m ->
                         when (m) {
-                            is ConversationMessageItem -> ConversationMessageItemView(m)
-                            is ConversationNoticeItem -> ConversationNoticeItemView(m)
+                            is ConversationMessageItem -> ConversationMessageItemView(m, viewModel::deleteMessage)
+                            is ConversationNoticeItem -> ConversationNoticeItemView(m, viewModel::deleteMessage)
                             is ConversationRequestItem ->
                                 ConversationRequestItemView(
                                     m,
                                     onResponse = { accept -> viewModel.respondToRequest(m, accept) },
+                                    onDelete = viewModel::deleteMessage,
                                 )
                         }
                     }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
index 7bad51e4e63a783bff1a785dd8d060758bbe1aa0..85da3d34147320b7a3d1cd65e8b3c736212362ac 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
@@ -310,6 +310,15 @@ constructor(
         }
     }
 
+    fun deleteMessage(id: MessageId) = runOnDbThread {
+        val result = conversationManager.deleteMessages(_contactId.value!!, listOf(id))
+        if (result.allDeleted()) {
+            _messages.removeIf { it.id == id }
+        } else {
+            _deletionResult.value = result
+        }
+    }
+
     fun deleteAllMessages() = runOnDbThread {
         _loadingMessages.value = true
         try {