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
@@ -229,18 +229,20 @@
   onResultsReceived: (
     messages: $ReadOnlyArray<RawMessageInfo>,
     endReached: boolean,
+    queryID: number,
     threadID: string,
   ) => mixed,
+  queryID: number,
   cursor?: string,
 ) => void {
   const callSearchMessages = useServerCall(searchMessages);
   const dispatchActionPromise = useDispatchActionPromise();
 
   return React.useCallback(
-    (query, threadID, onResultsReceived, cursor) => {
+    (query, threadID, onResultsReceived, queryID, cursor) => {
       const searchMessagesPromise = (async () => {
         if (query === '') {
-          onResultsReceived([], true, threadID);
+          onResultsReceived([], true, queryID, threadID);
           return;
         }
         const { messages, endReached } = await callSearchMessages({
@@ -248,7 +250,7 @@
           threadID,
           cursor,
         });
-        onResultsReceived(messages, endReached, threadID);
+        onResultsReceived(messages, endReached, queryID, threadID);
       })();
 
       dispatchActionPromise(searchMessagesActionTypes, searchMessagesPromise);
diff --git a/native/search/message-search.react.js b/native/search/message-search.react.js
--- a/native/search/message-search.react.js
+++ b/native/search/message-search.react.js
@@ -47,7 +47,14 @@
   const [endReached, setEndReached] = React.useState(false);
 
   const appendSearchResults = React.useCallback(
-    (newMessages: $ReadOnlyArray<RawMessageInfo>, end: boolean) => {
+    (
+      newMessages: $ReadOnlyArray<RawMessageInfo>,
+      end: boolean,
+      queryID: number,
+    ) => {
+      if (queryID !== queryIDRef.current) {
+        return;
+      }
       setSearchResults(oldMessages => [...oldMessages, ...newMessages]);
       setEndReached(end);
     },
@@ -56,16 +63,24 @@
 
   const searchMessages = useSearchMessages();
 
+  const queryIDRef = React.useRef(0);
+
   React.useEffect(() => {
     setSearchResults([]);
     setLastID(undefined);
     setEndReached(false);
   }, [query, searchMessages]);
 
-  React.useEffect(
-    () => searchMessages(query, threadInfo.id, appendSearchResults, lastID),
-    [appendSearchResults, lastID, query, searchMessages, threadInfo.id],
-  );
+  React.useEffect(() => {
+    queryIDRef.current += 1;
+    searchMessages(
+      query,
+      threadInfo.id,
+      appendSearchResults,
+      queryIDRef.current,
+      lastID,
+    );
+  }, [appendSearchResults, lastID, query, searchMessages, threadInfo.id]);
 
   const userInfos = useSelector(state => state.userStore.userInfos);
 
diff --git a/web/search/message-search-state-provider.react.js b/web/search/message-search-state-provider.react.js
--- a/web/search/message-search-state-provider.react.js
+++ b/web/search/message-search-state-provider.react.js
@@ -114,13 +114,19 @@
   const searchMessagesCall = useSearchMessages();
 
   const loading = React.useRef(false);
+  const queryIDRef = React.useRef(0);
 
   const appendResults = React.useCallback(
     (
       newMessages: $ReadOnlyArray<RawMessageInfo>,
       end: boolean,
+      queryID: number,
       threadID: string,
     ) => {
+      if (queryID !== queryIDRef.current) {
+        return;
+      }
+
       appendResult(newMessages, threadID);
       if (end) {
         setEndReached(threadID);
@@ -135,11 +141,13 @@
       if (loading.current || endsReached.current.has(threadID)) {
         return;
       }
+      queryIDRef.current += 1;
       loading.current = true;
       searchMessagesCall(
         queries.current[threadID],
         threadID,
         appendResults,
+        queryIDRef.current,
         lastIDs.current[threadID],
       );
     },