diff --git a/src/app/commoncomponents/DataTransferMessageDelegate.qml b/src/app/commoncomponents/DataTransferMessageDelegate.qml
index a87ee3447bff8baad2122d5c264ca1ab112c72fd..31fc011430300435c419f5e5af65b0a52e857724 100644
--- a/src/app/commoncomponents/DataTransferMessageDelegate.qml
+++ b/src/app/commoncomponents/DataTransferMessageDelegate.qml
@@ -22,7 +22,6 @@ import QtQuick
 import QtQuick.Controls
 import QtQuick.Layouts
 import Qt5Compat.GraphicalEffects
-
 import net.jami.Models 1.1
 import net.jami.Constants 1.1
 import net.jami.Adapters 1.1
@@ -44,12 +43,12 @@ Loader {
     property int transferStatus: TransferStatus
     onTidChanged: {
         if (tid === "") {
-            sourceComponent = deletedMsgComp
+            sourceComponent = deletedMsgComp;
         }
     }
     onTransferStatusChanged: {
         if (tid === "") {
-            sourceComponent = deletedMsgComp
+            sourceComponent = deletedMsgComp;
             return;
         } else if (transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED) {
             mediaInfo = MessagesAdapter.getMediaInfo(root.body);
@@ -64,7 +63,11 @@ Loader {
     width: ListView.view ? ListView.view.width : 0
 
     opacity: 0
-    Behavior on opacity { NumberAnimation { duration: 100 } }
+    Behavior on opacity {
+        NumberAnimation {
+            duration: 100
+        }
+    }
     onLoaded: opacity = 1
 
     Component {
@@ -93,9 +96,9 @@ Loader {
                     bottomPadding: 6
                     topPadding: 6
                     leftPadding: 10
-                    text: UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author) + " " + JamiStrings.deletedMedia ;
+                    text: UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author) + " " + JamiStrings.deletedMedia
                     horizontalAlignment: Text.AlignLeft
-                    width:  Math.min((2 / 3) * parent.width, implicitWidth + 18, innerContent.width - senderMargin + 18)
+                    width: Math.min((2 / 3) * parent.width, implicitWidth + 18, innerContent.width - senderMargin + 18)
 
                     font.pointSize: JamiTheme.smallFontSize
                     font.hintingPreference: Font.PreferNoHinting
@@ -107,8 +110,8 @@ Loader {
                     opacity: 0.5
 
                     function getBaseColor() {
-                        bubble.isDeleted = true
-                        return UtilsAdapter.luma(bubble.color) ? "white" : "dark"
+                        bubble.isDeleted = true;
+                        return UtilsAdapter.luma(bubble.color) ? "white" : "dark";
                     }
                 }
             ]
@@ -124,9 +127,7 @@ Loader {
             transferId: Id
             property var transferStats: MessagesAdapter.getTransferStats(transferId, root.transferStatus)
             property bool canOpen: root.transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED || isOutgoing
-            property real maxMsgWidth: root.width - senderMargin -
-                                       2 * hPadding - avatarBlockWidth
-                                       - buttonsLoader.width - 24 - 6 - 24
+            property real maxMsgWidth: root.width - senderMargin - 2 * hPadding - avatarBlockWidth - buttonsLoader.width - 24 - 6 - 24
 
             isOutgoing: Author === CurrentAccount.uri
             showTime: root.showTime
@@ -150,14 +151,12 @@ Loader {
                         enabled: canOpen
                         onHoveredChanged: {
                             if (enabled && hovered) {
-                                dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(location)
+                                dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(location);
                             } else {
-                                dataTransferItem.hoveredLink = ""
+                                dataTransferItem.hoveredLink = "";
                             }
                         }
-                        cursorShape: enabled ?
-                                         Qt.PointingHandCursor :
-                                         Qt.ArrowCursor
+                        cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
                     }
                     Loader {
                         id: buttonsLoader
@@ -171,21 +170,21 @@ Loader {
                             switch (root.transferStatus) {
                             case Interaction.TransferStatus.TRANSFER_CREATED:
                             case Interaction.TransferStatus.TRANSFER_FINISHED:
-                                iconSource = JamiResources.link_black_24dp_svg
-                                return terminatedComp
+                                iconSource = JamiResources.link_black_24dp_svg;
+                                return terminatedComp;
                             case Interaction.TransferStatus.TRANSFER_CANCELED:
                             case Interaction.TransferStatus.TRANSFER_ERROR:
                             case Interaction.TransferStatus.TRANSFER_UNJOINABLE_PEER:
                             case Interaction.TransferStatus.TRANSFER_TIMEOUT_EXPIRED:
                             case Interaction.TransferStatus.TRANSFER_AWAITING_HOST:
-                                iconSource = JamiResources.download_black_24dp_svg
-                                return optionsComp
+                                iconSource = JamiResources.download_black_24dp_svg;
+                                return optionsComp;
                             case Interaction.TransferStatus.TRANSFER_ONGOING:
-                                iconSource = JamiResources.close_black_24dp_svg
-                                return optionsComp
+                                iconSource = JamiResources.close_black_24dp_svg;
+                                return optionsComp;
                             default:
-                                iconSource = JamiResources.error_outline_black_24dp_svg
-                                return terminatedComp
+                                iconSource = JamiResources.error_outline_black_24dp_svg;
+                                return terminatedComp;
                             }
                         }
                         Component {
@@ -216,9 +215,9 @@ Loader {
                                 imageColor: JamiTheme.chatviewButtonColor
                                 onClicked: {
                                     if (root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) {
-                                        return MessagesAdapter.cancelFile(transferId)
+                                        return MessagesAdapter.cancelFile(transferId);
                                     } else {
-                                        return MessagesAdapter.acceptFile(transferId)
+                                        return MessagesAdapter.acceptFile(transferId);
                                     }
                                 }
                             }
@@ -230,27 +229,21 @@ Loader {
                         TextEdit {
                             width: Math.min(implicitWidth, maxMsgWidth)
                             topPadding: 10
-                            text: CurrentConversation.isSwarm ?
-                                      transferName :
-                                      location
+                            text: CurrentConversation.isSwarm ? transferName : location
                             wrapMode: Label.WrapAtWordBoundaryOrAnywhere
                             font.pointSize: 11
                             renderType: Text.NativeRendering
                             readOnly: true
-                            color: UtilsAdapter.luma(bubble.color)
-                                   ? JamiTheme.chatviewTextColorLight
-                                   : JamiTheme.chatviewTextColorDark
+                            color: UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
                             MouseArea {
                                 anchors.fill: parent
-                                cursorShape: canOpen ?
-                                                 Qt.PointingHandCursor :
-                                                 Qt.ArrowCursor
+                                cursorShape: canOpen ? Qt.PointingHandCursor : Qt.ArrowCursor
                                 onClicked: function (mouse) {
                                     if (canOpen) {
-                                        dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(location)
-                                        Qt.openUrlExternally(new URL(dataTransferItem.hoveredLink))
+                                        dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(location);
+                                        Qt.openUrlExternally(new URL(dataTransferItem.hoveredLink));
                                     } else {
-                                        dataTransferItem.hoveredLink = ""
+                                        dataTransferItem.hoveredLink = "";
                                     }
                                 }
                             }
@@ -261,23 +254,20 @@ Loader {
                             width: Math.min(implicitWidth, maxMsgWidth)
                             bottomPadding: 10
                             text: {
-                                var res = ""
+                                var res = "";
                                 if (transferStats.totalSize !== undefined) {
-                                    if (transferStats.progress !== 0 &&
-                                            transferStats.progress !== transferStats.totalSize) {
-                                        res += UtilsAdapter.humanFileSize(transferStats.progress) + " / "
+                                    if (transferStats.progress !== 0 && transferStats.progress !== transferStats.totalSize) {
+                                        res += UtilsAdapter.humanFileSize(transferStats.progress) + " / ";
                                     }
-                                    var totalSize = transferStats.totalSize !== 0 ? transferStats.totalSize : TotalSize
-                                    res += UtilsAdapter.humanFileSize(totalSize)
+                                    var totalSize = transferStats.totalSize !== 0 ? transferStats.totalSize : TotalSize;
+                                    res += UtilsAdapter.humanFileSize(totalSize);
                                 }
-                                return res
+                                return res;
                             }
                             wrapMode: Label.WrapAtWordBoundaryOrAnywhere
                             font.pointSize: 10
                             renderType: Text.NativeRendering
-                            color: UtilsAdapter.luma(bubble.color)
-                                   ? JamiTheme.chatviewTextColorLight
-                                   : JamiTheme.chatviewTextColorDark
+                            color: UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
                         }
                     }
                 },
@@ -316,23 +306,23 @@ Loader {
 
             Component.onCompleted: {
                 if (transferStats.totalSize !== undefined) {
-                    var totalSize = transferStats.totalSize !== 0 ? transferStats.totalSize : TotalSize
-                    var txt = UtilsAdapter.humanFileSize(totalSize)
+                    var totalSize = transferStats.totalSize !== 0 ? transferStats.totalSize : TotalSize;
+                    var txt = UtilsAdapter.humanFileSize(totalSize);
                 }
-                bubble.timestampItem.timeLabel.text += " - " + txt
-                bubble.color = "transparent"
+                bubble.timestampItem.timeLabel.text += " - " + txt;
+                bubble.color = "transparent";
                 if (mediaInfo.isImage)
-                    bubble.z = 1
+                    bubble.z = 1;
                 else
-                    timeUnderBubble = true
+                    timeUnderBubble = true;
             }
 
             onContentWidthChanged: {
                 if (bubble.timestampItem.timeLabel.width > contentWidth)
-                    timeUnderBubble = true
+                    timeUnderBubble = true;
                 else {
-                    bubble.timestampItem.timeColor = JamiTheme.whiteColor
-                    bubble.timestampItem.timeLabel.opacity = 1
+                    bubble.timestampItem.timeColor = JamiTheme.whiteColor;
+                    bubble.timestampItem.timeLabel.opacity = 1;
                 }
             }
 
@@ -346,10 +336,10 @@ Loader {
                     height: sourceComponent.height
                     sourceComponent: {
                         if (mediaInfo.isImage)
-                            return imageComp
+                            return imageComp;
                         if (mediaInfo.isAnimatedImage)
-                            return animatedImageComp
-                        return avComp
+                            return animatedImageComp;
+                        return avComp;
                     }
 
                     Component {
@@ -357,10 +347,11 @@ Loader {
 
                         Loader {
                             Component.onCompleted: {
-                                var qml = WITH_WEBENGINE ?
-                                            "qrc:/webengine/MediaPreviewBase.qml" :
-                                            "qrc:/nowebengine/MediaPreviewBase.qml"
-                                setSource( qml, { isVideo: mediaInfo.isVideo, html: mediaInfo.html } )
+                                var qml = WITH_WEBENGINE ? "qrc:/webengine/MediaPreviewBase.qml" : "qrc:/nowebengine/MediaPreviewBase.qml";
+                                setSource(qml, {
+                                        isVideo: mediaInfo.isVideo,
+                                        html: mediaInfo.html
+                                    });
                             }
                         }
                     }
@@ -381,9 +372,7 @@ Loader {
                             asynchronous: true
                             source: UtilsAdapter.urlFromLocalPath(Body)
                             property real aspectRatio: implicitWidth / implicitHeight
-                            property real adjustedWidth: Math.min(maxSize,
-                                                                  Math.max(minSize,
-                                                                           innerContent.width - senderMargin))
+                            property real adjustedWidth: Math.min(maxSize, Math.max(minSize, innerContent.width - senderMargin))
                             width: adjustedWidth
                             height: Math.ceil(adjustedWidth / aspectRatio)
                             Rectangle {
@@ -403,7 +392,7 @@ Loader {
                             }
 
                             onWidthChanged: {
-                                localMediaMsgItem.contentWidth = width
+                                localMediaMsgItem.contentWidth = width;
                             }
 
                             Component.onCompleted: localMediaMsgItem.bubble.imgSource = source
@@ -429,71 +418,54 @@ Loader {
                     Component {
                         id: imageComp
 
-                        Image {
-                            id: img
-
+                        Rectangle {
+                            border.color: img.useBox ? (JamiTheme.darkTheme ? "white" : JamiTheme.blackColor) : JamiTheme.transparentColor
+                            color: JamiTheme.transparentColor
                             anchors.right: isOutgoing ? parent.right : undefined
-                            cache: true
-                            fillMode: Image.PreserveAspectFit
-                            mipmap: true
-                            antialiasing: true
-                            autoTransform: true
-                            asynchronous: true
+                            border.width: 1
+                            radius: msgRadius
 
-                            Component.onCompleted: {
-                                source = UtilsAdapter.urlFromLocalPath(Body);
-                                localMediaMsgItem.bubble.imgSource = source;
+                            implicitWidth: img.width + (img.useBox ? 20 : 0)
+                            implicitHeight: img.height + (img.useBox ? 20 : 0)
+                            onWidthChanged: {
+                                localMediaMsgItem.contentWidth = width;
                             }
 
-                            // The sourceSize represents the maximum source dimensions.
-                            // This should not be a dynamic binding, as property changes
-                            // (resizing the chat view) here will trigger a reload of the image.
-                            sourceSize: Qt.size(256, 256)
-
-                            // Now we setup bindings for the destination image component size.
-                            // This based on the width available (width of the chat view), and
-                            // a restriction on the height.
-                            readonly property real aspectRatio: paintedWidth / paintedHeight
-                            readonly property real idealWidth: innerContent.width - senderMargin
-                            onStatusChanged: {
-                                if (img.status == Image.Ready && aspectRatio) {
-                                    height = Qt.binding(() => JamiQmlUtils.clamp(idealWidth / aspectRatio, 64, 256))
-                                    width = Qt.binding(() => height * aspectRatio)
-                                }
-                            }
+                            Image {
+                                id: img
 
-                            onWidthChanged: {
-                                localMediaMsgItem.contentWidth = width
-                            }
+                                anchors.centerIn: parent
+                                cache: true
+                                fillMode: Image.PreserveAspectFit
+                                mipmap: true
+                                antialiasing: true
+                                autoTransform: true
+                                asynchronous: true
 
-                            Rectangle {
-                                color: JamiTheme.previewImageBackgroundColor
-                                z: -1
-                                anchors.fill: parent
-                            }
-                            layer.enabled: true
-                            layer.effect: OpacityMask {
-                                maskSource: MessageBubble {
-                                    out: isOutgoing
-                                    type: seq
-                                    width: img.width
-                                    height: img.height
-                                    radius: msgRadius
+                                Component.onCompleted: {
+                                    source = UtilsAdapter.urlFromLocalPath(Body);
+                                    localMediaMsgItem.bubble.imgSource = source;
                                 }
-                            }
 
-                            LinearGradient {
-                                id: gradient
-                                anchors.fill: parent
-                                start: Qt.point(0, height / 3)
-                                gradient: Gradient {
-                                    GradientStop {
-                                        position: 0.0
-                                        color: JamiTheme.transparentColor
-                                    }
-                                    GradientStop {
-                                        position: 1.0
-                                        color: JamiTheme.darkGreyColorOpacityFade
+                                // Scale down the image if it's too wide or too tall.
+                                property real maxWidth: localMediaMsgItem.width - 170
+                                property bool xOverflow: sourceSize.width > maxWidth
+                                property bool yOverflow: sourceSize.height > JamiTheme.maxImageHeight
+                                property real scaleFactor: (xOverflow || yOverflow) ? Math.min(maxWidth / sourceSize.width, JamiTheme.maxImageHeight / sourceSize.height) : 1
+                                width: sourceSize.width * scaleFactor
+                                height: sourceSize.height * scaleFactor
+
+                                // Add a bounding box around the image if it's small (along at least one
+                                // dimension) to ensure that it's easy for users to see it and click on it.
+                                property bool useBox: (paintedWidth < 40) || (paintedHeight < 40)
+                                layer.enabled: !useBox
+                                layer.effect: OpacityMask {
+                                    maskSource: MessageBubble {
+                                        out: isOutgoing
+                                        type: seq
+                                        width: img.width
+                                        height: img.height
+                                        radius: msgRadius
                                     }
                                 }
                             }
diff --git a/src/app/net/jami/Constants/JamiTheme.qml b/src/app/net/jami/Constants/JamiTheme.qml
index 2f148072cb3bf13d0b0b1d0be3724ab77087a034..649ccc0d4eedbff6638ed67c7932a3922aa8d829 100644
--- a/src/app/net/jami/Constants/JamiTheme.qml
+++ b/src/app/net/jami/Constants/JamiTheme.qml
@@ -255,6 +255,7 @@ Item {
     property color messageWebViewFooterButtonImageColor: darkTheme ? "#838383" : "#656565"
     property color chatviewSecondaryInformationColor: "#A7A7A7"
     property color draftIconColor: "#707070"
+    property real maxImageHeight: 375
 
     // ChatView Footer
     property color chatViewFooterListColor: darkTheme ? blackColor : "#E5E5E5"