diff --git a/native/chat/settings/thread-settings-media-gallery.react.js b/native/chat/settings/thread-settings-media-gallery.react.js
--- a/native/chat/settings/thread-settings-media-gallery.react.js
+++ b/native/chat/settings/thread-settings-media-gallery.react.js
@@ -1,16 +1,26 @@
 // @flow
 
+import { useNavigation, useRoute } from '@react-navigation/native';
 import * as React from 'react';
 import { View, useWindowDimensions } from 'react-native';
+import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet';
 import { FlatList } from 'react-native-gesture-handler';
 
 import { fetchThreadMedia } from 'lib/actions/thread-actions.js';
-import type { MediaInfo } from 'lib/types/media-types';
+import type { MediaInfo, Media } from 'lib/types/media-types';
 import { useServerCall } from 'lib/utils/action-utils.js';
 
 import GestureTouchableOpacity from '../../components/gesture-touchable-opacity.react.js';
 import Multimedia from '../../media/multimedia.react.js';
+import {
+  ImageModalRouteName,
+  VideoPlaybackModalRouteName,
+} from '../../navigation/route-names.js';
 import { useStyles } from '../../themes/colors.js';
+import type {
+  LayoutCoordinates,
+  VerticalBounds,
+} from '../../types/layout-types.js';
 
 const galleryItemGap = 8;
 const numColumns = 3;
@@ -18,6 +28,7 @@
 type ThreadSettingsMediaGalleryProps = {
   +threadID: string,
   +limit: number,
+  +verticalBounds: ?VerticalBounds,
 };
 
 function ThreadSettingsMediaGallery(
@@ -36,7 +47,7 @@
   const galleryItemWidth =
     (width - 32 - (numColumns - 1) * galleryItemGap) / numColumns;
 
-  const { threadID, limit } = props;
+  const { threadID, limit, verticalBounds } = props;
   const [mediaInfos, setMediaInfos] = React.useState([]);
   const callFetchThreadMedia = useServerCall(fetchThreadMedia);
 
@@ -73,30 +84,16 @@
   }, [galleryItemWidth, styles.media, styles.mediaContainer]);
 
   const renderItem = React.useCallback(
-    ({ item, index }) => {
-      const containerStyle =
-        index % numColumns === 0
-          ? memoizedStyles.mediaContainer
-          : memoizedStyles.mediaContainerWithMargin;
-
-      const mediaInfoItem: MediaInfo = {
-        ...item,
-        index,
-      };
-
-      return (
-        <View key={item.id} style={containerStyle}>
-          <GestureTouchableOpacity style={memoizedStyles.media}>
-            <Multimedia mediaInfo={mediaInfoItem} spinnerColor="black" />
-          </GestureTouchableOpacity>
-        </View>
-      );
-    },
-    [
-      memoizedStyles.media,
-      memoizedStyles.mediaContainer,
-      memoizedStyles.mediaContainerWithMargin,
-    ],
+    ({ item, index }) => (
+      <MediaGalleryItem
+        item={item}
+        index={index}
+        memoizedStyles={memoizedStyles}
+        threadID={threadID}
+        verticalBounds={verticalBounds}
+      />
+    ),
+    [threadID, verticalBounds, memoizedStyles],
   );
 
   return (
@@ -110,6 +107,76 @@
   );
 }
 
+type MediaGalleryItemProps = {
+  +item: Media,
+  +index: number,
+  +memoizedStyles: {
+    +mediaContainer: ViewStyleProp,
+    +mediaContainerWithMargin: ViewStyleProp,
+    +media: ViewStyleProp,
+  },
+  +threadID: string,
+  +verticalBounds: ?VerticalBounds,
+};
+
+function MediaGalleryItem(props: MediaGalleryItemProps): React.Node {
+  const navigation = useNavigation();
+  const route = useRoute();
+  const ref = React.useRef(null);
+  const onLayout = React.useCallback(() => {}, []);
+  const { threadID, verticalBounds, memoizedStyles, item, index } = props;
+
+  const mediaInfo: MediaInfo = React.useMemo(
+    () => ({
+      ...(item: Media),
+      index,
+    }),
+    [item, index],
+  );
+
+  const navigateToMedia = React.useCallback(() => {
+    ref.current?.measure((x, y, width, height, pageX, pageY) => {
+      const initialCoordinates: LayoutCoordinates = {
+        x: pageX,
+        y: pageY,
+        width,
+        height,
+      };
+
+      navigation.navigate<'VideoPlaybackModal' | 'ImageModal'>({
+        name:
+          mediaInfo.type === 'video'
+            ? VideoPlaybackModalRouteName
+            : ImageModalRouteName,
+        key: `multimedia|${threadID}|${mediaInfo.id}`,
+        params: {
+          presentedFrom: route.key,
+          mediaInfo,
+          item,
+          initialCoordinates,
+          verticalBounds,
+        },
+      });
+    });
+  }, [navigation, route, threadID, mediaInfo, item, verticalBounds]);
+
+  const containerStyle =
+    index % numColumns === 0
+      ? memoizedStyles.mediaContainer
+      : memoizedStyles.mediaContainerWithMargin;
+
+  return (
+    <View key={item.id} style={containerStyle} onLayout={onLayout} ref={ref}>
+      <GestureTouchableOpacity
+        onPress={navigateToMedia}
+        style={memoizedStyles.media}
+      >
+        <Multimedia mediaInfo={mediaInfo} spinnerColor="black" />
+      </GestureTouchableOpacity>
+    </View>
+  );
+}
+
 const unboundStyles = {
   flatListContainer: {
     paddingHorizontal: 16,
diff --git a/native/chat/settings/thread-settings.react.js b/native/chat/settings/thread-settings.react.js
--- a/native/chat/settings/thread-settings.react.js
+++ b/native/chat/settings/thread-settings.react.js
@@ -208,6 +208,7 @@
       +key: string,
       +threadInfo: ThreadInfo,
       +limit: number,
+      +verticalBounds: ?VerticalBounds,
     }
   | {
       +itemType: 'promoteSidebar' | 'leaveThread' | 'deleteThread',
@@ -635,7 +636,8 @@
 
   mediaGalleryListDataSelector = createSelector(
     (propsAndState: PropsAndState) => propsAndState.threadInfo,
-    (threadInfo: ThreadInfo) => {
+    (propsAndState: PropsAndState) => propsAndState.verticalBounds,
+    (threadInfo: ThreadInfo, verticalBounds: ?VerticalBounds) => {
       const listData: ChatSettingsItem[] = [];
       const limit = 6;
 
@@ -652,6 +654,7 @@
         key: 'mediaGallery',
         threadInfo,
         limit,
+        verticalBounds,
       });
 
       listData.push({
@@ -953,6 +956,7 @@
         <ThreadSettingsMediaGallery
           threadID={item.threadInfo.id}
           limit={item.limit}
+          verticalBounds={item.verticalBounds}
         />
       );
     } else if (item.itemType === 'leaveThread') {