diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js
index 4ae55b49ea0a..d19b3f6ce05d 100644
--- a/src/components/ImageView/index.js
+++ b/src/components/ImageView/index.js
@@ -1,35 +1,145 @@
-import React, {memo} from 'react';
+import React, {PureComponent} from 'react';
 import PropTypes from 'prop-types';
-import {View, Image} from 'react-native';
-import styles from '../../styles/styles';
+import {View, Image, Pressable} from 'react-native';
+import styles, {getZoomCursorStyle, getZoomSizingStyle} from '../../styles/styles';
+import canUseTouchScreen from '../../libs/canUseTouchscreen';
 
 const propTypes = {
     // URL to full-sized image
     url: PropTypes.string.isRequired,
 };
 
-const ImageView = props => (
-    <View
-        style={[
+class ImageView extends PureComponent {
+    constructor(props) {
+        super(props);
+        this.scrollableRef = null;
+        this.canUseTouchScreen = canUseTouchScreen();
+        this.containerStyles = [
             styles.w100,
             styles.h100,
             styles.alignItemsCenter,
             styles.justifyContentCenter,
-            styles.overflowHidden,
-        ]}
-    >
-        <Image
-            source={{uri: props.url}}
-            style={[
-                styles.w100,
-                styles.h100,
-            ]}
-            resizeMode="center"
-        />
-    </View>
-);
+        ];
+        this.state = {
+            isZoomed: false,
+            isDragging: false,
+            isMouseDown: false,
+            initialScrollLeft: 0,
+            initialScrollTop: 0,
+            initialX: 0,
+            initialY: 0,
+        };
+    }
 
-ImageView.propTypes = propTypes;
-ImageView.displayName = 'ImageView';
+    componentDidMount() {
+        if (this.canUseTouchScreen) {
+            return;
+        }
+
+        document.addEventListener('mousemove', this.trackMovement.bind(this));
+    }
+
+    componentWillUnmount() {
+        if (this.canUseTouchScreen) {
+            return;
+        }
+
+        document.removeEventListener('mousemove', this.trackMovement.bind(this));
+    }
+
+    trackMovement(e) {
+        if (!this.state.isZoomed) {
+            return;
+        }
+
+        if (this.state.isDragging && this.state.isMouseDown) {
+            const x = e.nativeEvent.x;
+            const y = e.nativeEvent.y;
+            const moveX = this.state.initialX - x;
+            const moveY = this.state.initialY - y;
+            this.scrollableRef.scrollLeft = this.state.initialScrollLeft + moveX;
+            this.scrollableRef.scrollTop = this.state.initialScrollTop + moveY;
+        }
 
-export default memo(ImageView);
+        this.setState(prevState => ({isDragging: prevState.isMouseDown}));
+    }
+
+    render() {
+        if (this.canUseTouchScreen) {
+            return (
+                <View
+                    style={[...this.containerStyles, styles.overflowHidden]}
+                >
+                    <Image
+                        source={{uri: this.props.url}}
+                        style={[
+                            styles.w100,
+                            styles.h100,
+                        ]}
+                        resizeMode="center"
+                    />
+                </View>
+            );
+        }
+
+        return (
+            <View
+                ref={el => this.scrollableRef = el}
+                style={[
+                    ...this.containerStyles,
+                    styles.overflowScroll,
+                    styles.noScrollbars,
+                ]}
+            >
+                <Pressable
+                    style={[
+                        styles.w100,
+                        styles.h100,
+                        styles.flex1,
+                        getZoomCursorStyle(this.state.isZoomed, this.state.isDragging),
+                    ]}
+                    onPressIn={(e) => {
+                        const {pageX, pageY} = e.nativeEvent;
+                        this.setState({
+                            isMouseDown: true,
+                            initialX: pageX,
+                            initialY: pageY,
+                            initialScrollLeft: this.scrollableRef.scrollLeft,
+                            initialScrollTop: this.scrollableRef.scrollTop,
+                        });
+                    }}
+                    onPress={(e) => {
+                        if (this.state.isZoomed && !this.state.isDragging) {
+                            const {offsetX, offsetY} = e.nativeEvent;
+                            this.scrollableRef.scrollTop = offsetY * 1.5;
+                            this.scrollableRef.scrollLeft = offsetX * 1.5;
+                        }
+
+                        if (this.state.isZoomed && this.state.isDragging && this.state.isMouseDown) {
+                            this.setState({isDragging: false, isMouseDown: false});
+                        }
+                    }}
+                    onPressOut={() => {
+                        if (this.state.isDragging) {
+                            return;
+                        }
+
+                        this.setState(prevState => ({
+                            isZoomed: !prevState.isZoomed,
+                            isMouseDown: false,
+                        }));
+                    }}
+                >
+                    <Image
+                        source={{uri: this.props.url}}
+                        style={getZoomSizingStyle(this.state.isZoomed)}
+                        resizeMode="center"
+                    />
+                </Pressable>
+            </View>
+        );
+    }
+}
+
+ImageView.propTypes = propTypes;
+export default ImageView;
diff --git a/src/libs/canUseTouchscreen.js b/src/libs/canUseTouchscreen.js
new file mode 100644
index 000000000000..a3310a5d6be1
--- /dev/null
+++ b/src/libs/canUseTouchscreen.js
@@ -0,0 +1,32 @@
+/**
+ * Allows us to identify whether the platform has a touchscreen.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
+ *
+ * @returns {Boolean}
+ */
+function canUseTouchScreen() {
+    let hasTouchScreen = false;
+    if ('maxTouchPoints' in navigator) {
+        hasTouchScreen = navigator.maxTouchPoints > 0;
+    } else if ('msMaxTouchPoints' in navigator) {
+        hasTouchScreen = navigator.msMaxTouchPoints > 0;
+    } else {
+        const mQ = window.matchMedia && matchMedia('(pointer:coarse)');
+        if (mQ && mQ.media === '(pointer:coarse)') {
+            hasTouchScreen = !!mQ.matches;
+        } else if ('orientation' in window) {
+            hasTouchScreen = true; // deprecated, but good fallback
+        } else {
+            // Only as a last resort, fall back to user agent sniffing
+            const UA = navigator.userAgent;
+            hasTouchScreen = (
+                /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA)
+                || /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA)
+            );
+        }
+    }
+    return hasTouchScreen;
+}
+
+export default canUseTouchScreen;
diff --git a/src/styles/styles.js b/src/styles/styles.js
index c21bffe0ff83..bd35955c8538 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -1185,6 +1185,10 @@ const styles = {
         fontWeight: fontWeightBold,
         fontSize: variables.iouAmountTextSize,
     }, 0),
+
+    noScrollbars: {
+        scrollbarWidth: 'none',
+    },
 };
 
 const baseCodeTagStyles = {
@@ -1322,6 +1326,32 @@ function getNavigationModalCardStyle(isSmallScreenWidth) {
     };
 }
 
+/**
+ * @param {Boolean} isZoomed
+ * @param {Boolean} isDragging
+ * @return {Object}
+ */
+function getZoomCursorStyle(isZoomed, isDragging) {
+    if (!isZoomed) {
+        return {cursor: 'zoom-in'};
+    }
+
+    return {
+        cursor: isDragging ? 'grabbing' : 'zoom-out',
+    };
+}
+
+/**
+ * @param {Boolean} isZoomed
+ * @return {Object}
+ */
+function getZoomSizingStyle(isZoomed) {
+    return {
+        height: isZoomed ? '250%' : '100%',
+        width: isZoomed ? '250%' : '100%',
+    };
+}
+
 export default styles;
 export {
     getSafeAreaPadding,
@@ -1330,4 +1360,6 @@ export {
     getNavigationDrawerStyle,
     getNavigationDrawerType,
     getNavigationModalCardStyle,
+    getZoomCursorStyle,
+    getZoomSizingStyle,
 };
