diff --git a/lib/shared/search-utils.js b/lib/shared/search-utils.js
--- a/lib/shared/search-utils.js
+++ b/lib/shared/search-utils.js
@@ -1,6 +1,7 @@
 // @flow
 
 import * as React from 'react';
+import { useSelector } from 'react-redux';
 
 import SearchIndex from './search-index.js';
 import {
@@ -12,6 +13,10 @@
   searchMessages,
   searchMessagesActionTypes,
 } from '../actions/message-actions.js';
+import {
+  searchUsers,
+  searchUsersActionTypes,
+} from '../actions/user-actions.js';
 import genesis from '../facts/genesis.js';
 import type { RawMessageInfo } from '../types/message-types.js';
 import { userRelationshipStatus } from '../types/relationship-types.js';
@@ -243,4 +248,43 @@
   );
 }
 
-export { getPotentialMemberItems, notFriendNotice, useSearchMessages };
+function useSearchUsers(
+  usernameInputText: string,
+): $ReadOnlyArray<GlobalAccountUserInfo> {
+  const currentUserID = useSelector(
+    state => state.currentUserInfo && state.currentUserInfo.id,
+  );
+
+  const [serverSearchResults, setServerSearchResults] = React.useState<
+    $ReadOnlyArray<GlobalAccountUserInfo>,
+  >([]);
+  const callSearchUsers = useServerCall(searchUsers);
+  const dispatchActionPromise = useDispatchActionPromise();
+  React.useEffect(() => {
+    const searchUsersPromise = (async () => {
+      if (usernameInputText.length === 0) {
+        setServerSearchResults([]);
+      } else {
+        const { userInfos } = await callSearchUsers(usernameInputText);
+        setServerSearchResults(
+          userInfos.filter(({ id }) => id !== currentUserID),
+        );
+      }
+    })();
+    dispatchActionPromise(searchUsersActionTypes, searchUsersPromise);
+  }, [
+    callSearchUsers,
+    currentUserID,
+    dispatchActionPromise,
+    usernameInputText,
+  ]);
+
+  return serverSearchResults;
+}
+
+export {
+  getPotentialMemberItems,
+  notFriendNotice,
+  useSearchMessages,
+  useSearchUsers,
+};
diff --git a/lib/types/user-types.js b/lib/types/user-types.js
--- a/lib/types/user-types.js
+++ b/lib/types/user-types.js
@@ -131,11 +131,9 @@
 };
 
 export type UserListItem = {
-  +id: string,
-  +username: string,
+  ...AccountUserInfo,
   +disabled?: boolean,
   +notice?: string,
   +alertText?: string,
   +alertTitle?: string,
-  +avatar?: ?ClientAvatar,
 };
diff --git a/native/chat/compose-subchannel.react.js b/native/chat/compose-subchannel.react.js
--- a/native/chat/compose-subchannel.react.js
+++ b/native/chat/compose-subchannel.react.js
@@ -248,13 +248,13 @@
   );
 
   const onUserSelect = React.useCallback(
-    (userID: string) => {
-      if (userInfoInputIDs.some(existingUserID => userID === existingUserID)) {
+    ({ id }: AccountUserInfo) => {
+      if (userInfoInputIDs.some(existingUserID => id === existingUserID)) {
         return;
       }
       setUserInfoInputArray(oldUserInfoInputArray => [
         ...oldUserInfoInputArray,
-        otherUserInfos[userID],
+        otherUserInfos[id],
       ]);
       setUsernameInputText('');
     },
diff --git a/native/chat/message-list-container.react.js b/native/chat/message-list-container.react.js
--- a/native/chat/message-list-container.react.js
+++ b/native/chat/message-list-container.react.js
@@ -12,7 +12,10 @@
   userInfoSelectorForPotentialMembers,
   userSearchIndexForPotentialMembers,
 } from 'lib/selectors/user-selectors.js';
-import { getPotentialMemberItems } from 'lib/shared/search-utils.js';
+import {
+  getPotentialMemberItems,
+  useSearchUsers,
+} from 'lib/shared/search-utils.js';
 import {
   useExistingThreadInfoFinder,
   pendingThreadType,
@@ -58,7 +61,6 @@
   +userInfoInputArray: $ReadOnlyArray<AccountUserInfo>,
   +updateTagInput: (items: $ReadOnlyArray<AccountUserInfo>) => void,
   +resolveToUser: (user: AccountUserInfo) => void,
-  +otherUserInfos: { [id: string]: AccountUserInfo },
   +userSearchResults: $ReadOnlyArray<UserListItem>,
   +threadInfo: ThreadInfo,
   +genesisThreadInfo: ?ThreadInfo,
@@ -154,7 +156,6 @@
             userInfoInputArray={userInfoInputArray}
             updateTagInput={this.props.updateTagInput}
             resolveToUser={this.props.resolveToUser}
-            otherUserInfos={this.props.otherUserInfos}
             userSearchResults={this.props.userSearchResults}
           />
         </>
@@ -249,16 +250,24 @@
 
     const otherUserInfos = useSelector(userInfoSelectorForPotentialMembers);
     const userSearchIndex = useSelector(userSearchIndexForPotentialMembers);
-    const userSearchResults = React.useMemo(
-      () =>
-        getPotentialMemberItems({
-          text: usernameInputText,
-          userInfos: otherUserInfos,
-          searchIndex: userSearchIndex,
-          excludeUserIDs: userInfoInputArray.map(userInfo => userInfo.id),
-        }),
-      [usernameInputText, otherUserInfos, userSearchIndex, userInfoInputArray],
-    );
+
+    const serverSearchResults = useSearchUsers(usernameInputText);
+
+    const userSearchResults = React.useMemo(() => {
+      return getPotentialMemberItems({
+        text: usernameInputText,
+        userInfos: otherUserInfos,
+        searchIndex: userSearchIndex,
+        excludeUserIDs: userInfoInputArray.map(userInfo => userInfo.id),
+        includeServerSearchUsers: serverSearchResults,
+      });
+    }, [
+      usernameInputText,
+      otherUserInfos,
+      userSearchIndex,
+      userInfoInputArray,
+      serverSearchResults,
+    ]);
 
     const [baseThreadInfo, setBaseThreadInfo] = React.useState(
       props.route.params.threadInfo,
@@ -421,7 +430,6 @@
           userInfoInputArray={userInfoInputArray}
           updateTagInput={updateTagInput}
           resolveToUser={resolveToUser}
-          otherUserInfos={otherUserInfos}
           userSearchResults={userSearchResults}
           threadInfo={threadInfo}
           genesisThreadInfo={genesisThreadInfo}
diff --git a/native/chat/message-list-thread-search.react.js b/native/chat/message-list-thread-search.react.js
--- a/native/chat/message-list-thread-search.react.js
+++ b/native/chat/message-list-thread-search.react.js
@@ -19,7 +19,6 @@
   +userInfoInputArray: $ReadOnlyArray<AccountUserInfo>,
   +updateTagInput: (items: $ReadOnlyArray<AccountUserInfo>) => void,
   +resolveToUser: (user: AccountUserInfo) => void,
-  +otherUserInfos: { [id: string]: AccountUserInfo },
   +userSearchResults: $ReadOnlyArray<UserListItem>,
 };
 
@@ -36,7 +35,6 @@
     userInfoInputArray,
     updateTagInput,
     resolveToUser,
-    otherUserInfos,
     userSearchResults,
   }) {
     const styles = useStyles(unboundStyles);
@@ -61,14 +59,13 @@
     }, [userSearchResults, userInfoInputArray]);
 
     const onUserSelect = React.useCallback(
-      (userID: string) => {
+      (userInfo: AccountUserInfo) => {
         for (const existingUserInfo of userInfoInputArray) {
-          if (userID === existingUserInfo.id) {
+          if (userInfo.id === existingUserInfo.id) {
             return;
           }
         }
-        const userInfo = otherUserInfos[userID];
-        if (nonFriends.has(userID)) {
+        if (nonFriends.has(userInfo.id)) {
           resolveToUser(userInfo);
           return;
         }
@@ -79,7 +76,6 @@
       [
         userInfoInputArray,
         nonFriends,
-        otherUserInfos,
         updateTagInput,
         resolveToUser,
         updateUsernameInput,
diff --git a/native/chat/settings/add-users-modal.react.js b/native/chat/settings/add-users-modal.react.js
--- a/native/chat/settings/add-users-modal.react.js
+++ b/native/chat/settings/add-users-modal.react.js
@@ -210,16 +210,16 @@
   );
 
   const onUserSelect = React.useCallback(
-    (userID: string) => {
+    ({ id }: AccountUserInfo) => {
       if (isLoading) {
         return;
       }
-      if (userInfoInputIDs.some(existingUserID => userID === existingUserID)) {
+      if (userInfoInputIDs.some(existingUserID => id === existingUserID)) {
         return;
       }
       setUserInfoInputArray(oldUserInfoInputArray => [
         ...oldUserInfoInputArray,
-        otherUserInfos[userID],
+        otherUserInfos[id],
       ]);
       setUsernameInputText('');
     },
diff --git a/native/components/user-list-user.react.js b/native/components/user-list-user.react.js
--- a/native/components/user-list-user.react.js
+++ b/native/components/user-list-user.react.js
@@ -3,7 +3,7 @@
 import * as React from 'react';
 import { Text, Platform, Alert } from 'react-native';
 
-import type { UserListItem } from 'lib/types/user-types.js';
+import type { UserListItem, AccountUserInfo } from 'lib/types/user-types.js';
 
 import Button from './button.react.js';
 import { SingleLine } from './single-line.react.js';
@@ -19,7 +19,7 @@
 
 type BaseProps = {
   +userInfo: UserListItem,
-  +onSelect: (userID: string) => void,
+  +onSelect: (user: AccountUserInfo) => void,
   +textStyle?: TextStyle,
 };
 type Props = {
@@ -58,7 +58,9 @@
   onSelect = () => {
     const { userInfo } = this.props;
     if (!userInfo.alertText) {
-      this.props.onSelect(userInfo.id);
+      const { alertText, alertTitle, notice, disabled, ...accountUserInfo } =
+        userInfo;
+      this.props.onSelect(accountUserInfo);
       return;
     }
     Alert.alert(userInfo.alertTitle, userInfo.alertText, [{ text: 'OK' }], {
diff --git a/native/components/user-list.react.js b/native/components/user-list.react.js
--- a/native/components/user-list.react.js
+++ b/native/components/user-list.react.js
@@ -4,7 +4,7 @@
 import * as React from 'react';
 import { FlatList } from 'react-native';
 
-import type { UserListItem } from 'lib/types/user-types.js';
+import type { UserListItem, AccountUserInfo } from 'lib/types/user-types.js';
 
 import { UserListUser, getUserListItemHeight } from './user-list-user.react.js';
 import { type IndicatorStyle, useIndicatorStyle } from '../themes/colors.js';
@@ -12,7 +12,7 @@
 
 type BaseProps = {
   +userInfos: $ReadOnlyArray<UserListItem>,
-  +onSelect: (userID: string) => void,
+  +onSelect: (user: AccountUserInfo) => void,
   +itemTextStyle?: TextStyle,
 };
 type Props = {