diff --git a/native/chat/typeahead-tooltip.react.js b/native/chat/typeahead-tooltip.react.js
new file mode 100644
--- /dev/null
+++ b/native/chat/typeahead-tooltip.react.js
@@ -0,0 +1,148 @@
+// @flow
+
+import * as React from 'react';
+import { Platform, Text } from 'react-native';
+import { PanGestureHandler, FlatList } from 'react-native-gesture-handler';
+
+import {
+  type TypeaheadMatchedStrings,
+  type Selection,
+  getNewTextAndSelection,
+} from 'lib/shared/typeahead-utils';
+import type { RelativeMemberInfo } from 'lib/types/thread-types';
+
+import Button from '../components/button.react';
+import { useStyles } from '../themes/colors';
+
+export type TypeaheadTooltipProps = {
+  +text: string,
+  +matchedStrings: TypeaheadMatchedStrings,
+  +suggestedUsers: $ReadOnlyArray<RelativeMemberInfo>,
+  +focusAndUpdateTextAndSelection: (text: string, selection: Selection) => void,
+};
+
+function TypeaheadTooltip(props: TypeaheadTooltipProps): React.Node {
+  const {
+    text,
+    matchedStrings,
+    suggestedUsers,
+    focusAndUpdateTextAndSelection,
+  } = props;
+
+  const { textBeforeAtSymbol, usernamePrefix } = matchedStrings;
+
+  const styles = useStyles(unboundStyles);
+
+  const renderTypeaheadButton = React.useCallback(
+    ({ item }: { item: RelativeMemberInfo, ... }) => {
+      const onPress = () => {
+        const { newText, newSelectionStart } = getNewTextAndSelection(
+          textBeforeAtSymbol,
+          text,
+          usernamePrefix,
+          item,
+        );
+
+        focusAndUpdateTextAndSelection(newText, {
+          start: newSelectionStart,
+          end: newSelectionStart,
+        });
+      };
+
+      return (
+        <Button onPress={onPress} style={styles.button} iosActiveOpacity={0.85}>
+          <Text style={styles.buttonLabel} numberOfLines={1}>
+            @{item.username}
+          </Text>
+        </Button>
+      );
+    },
+    [
+      focusAndUpdateTextAndSelection,
+      styles.button,
+      styles.buttonLabel,
+      text,
+      textBeforeAtSymbol,
+      usernamePrefix,
+    ],
+  );
+
+  // This is a hack that was introduced due to a buggy behavior of a
+  // absolutely positioned FlatList on Android.
+
+  // There was a bug that was present when there were too few items in a
+  // FlatList and it wasn't scrollable. It was only present on Android as
+  // iOS has a default "bounce" animation, even if the list is too short.
+  // The bug manifested itself when we tried to scroll the FlatList.
+  // Because it was unscrollable we were really scrolling FlatList
+  // below it (in the ChatList) as FlatList here has "position: absolute"
+  // and is positioned over the other FlatList.
+
+  // The hack here solves it by using a PanGestureHandler. This way Pan events
+  // on TypeaheadTooltip FlatList are always caught by handler.
+  // When the FlatList is scrollable it scrolls normally, because handler
+  // passes those events down to it.
+
+  // If it's not scrollable, the PanGestureHandler "swallows" them.
+  // Normally it would trigger onGestureEvent callback, but we don't need to
+  // handle those events. We just want them to be ignored
+  // and that's what's actually happening.
+
+  const flatList = React.useMemo(
+    () => (
+      <FlatList
+        style={styles.container}
+        contentContainerStyle={styles.contentContainer}
+        data={suggestedUsers}
+        renderItem={renderTypeaheadButton}
+        keyboardShouldPersistTaps="always"
+      />
+    ),
+    [
+      renderTypeaheadButton,
+      styles.container,
+      styles.contentContainer,
+      suggestedUsers,
+    ],
+  );
+
+  const listWithConditionalHandler = React.useMemo(() => {
+    if (Platform.OS === 'android') {
+      return <PanGestureHandler>{flatList}</PanGestureHandler>;
+    }
+    return flatList;
+  }, [flatList]);
+
+  return listWithConditionalHandler;
+}
+
+const unboundStyles = {
+  container: {
+    position: 'absolute',
+    maxHeight: 200,
+    left: 0,
+    right: 0,
+    bottom: '100%',
+    backgroundColor: 'typeaheadTooltipBackground',
+    borderBottomWidth: 1,
+    borderTopWidth: 1,
+    borderColor: 'typeaheadTooltipBorder',
+    borderStyle: 'solid',
+  },
+  contentContainer: {
+    padding: 8,
+  },
+  button: {
+    flexDirection: 'row',
+    innerHeight: 24,
+    padding: 8,
+    color: 'typeaheadTooltipText',
+  },
+  buttonLabel: {
+    color: 'white',
+    fontSize: 16,
+    fontWeight: '400',
+  },
+};
+
+export default TypeaheadTooltip;
diff --git a/native/themes/colors.js b/native/themes/colors.js
--- a/native/themes/colors.js
+++ b/native/themes/colors.js
@@ -175,6 +175,9 @@
   subthreadsModalClose: '#808080',
   subthreadsModalBackground: '#1F1F1F',
   subthreadsModalSearch: '#FFFFFF04',
+  typeaheadTooltipBackground: '#1F1F1f',
+  typeaheadTooltipBorder: '#404040',
+  typeaheadTooltipText: 'white',
 });
 const colors = { light, dark };