diff --git a/lib/shared/message-utils.js b/lib/shared/message-utils.js
--- a/lib/shared/message-utils.js
+++ b/lib/shared/message-utils.js
@@ -25,7 +25,10 @@
 } from '../types/message-types';
 import type { ImagesMessageData } from '../types/messages/images';
 import type { MediaMessageData } from '../types/messages/media';
-import type { ReactionMessageInfo } from '../types/messages/reaction';
+import type {
+  RawReactionMessageInfo,
+  ReactionMessageInfo,
+} from '../types/messages/reaction';
 import { type ThreadInfo } from '../types/thread-types';
 import type { RelativeUserInfo, UserInfos } from '../types/user-types';
 import { codeBlockRegex, type ParserRules } from './markdown';
@@ -359,7 +362,9 @@
   return result;
 }
 
-function stripLocalID(rawMessageInfo: RawComposableMessageInfo) {
+function stripLocalID(
+  rawMessageInfo: RawComposableMessageInfo | RawReactionMessageInfo,
+) {
   const { localID, ...rest } = rawMessageInfo;
   return rest;
 }
diff --git a/lib/types/message-types.js b/lib/types/message-types.js
--- a/lib/types/message-types.js
+++ b/lib/types/message-types.js
@@ -192,7 +192,8 @@
   if (
     messageData.type !== messageTypes.TEXT &&
     messageData.type !== messageTypes.IMAGES &&
-    messageData.type !== messageTypes.MULTIMEDIA
+    messageData.type !== messageTypes.MULTIMEDIA &&
+    messageData.type !== messageTypes.REACTION
   ) {
     return null;
   }
@@ -294,7 +295,11 @@
   | ({
       ...RawTextMessageInfo,
       +localID: string,
-    } & RawTextMessageInfo);
+    } & RawTextMessageInfo)
+  | ({
+      ...RawReactionMessageInfo,
+      +localID: string,
+    } & RawReactionMessageInfo);
 
 export type MultimediaMessageInfo = ImagesMessageInfo | MediaMessageInfo;
 export type ComposableMessageInfo = TextMessageInfo | MultimediaMessageInfo;
@@ -551,6 +556,7 @@
 
 export type SendReactionMessageRequest = {
   +threadID: string,
+  +localID?: string,
   +targetMessageID: string,
   +reaction: string,
   +action: 'add_reaction' | 'remove_reaction',
diff --git a/lib/types/messages/reaction.js b/lib/types/messages/reaction.js
--- a/lib/types/messages/reaction.js
+++ b/lib/types/messages/reaction.js
@@ -4,6 +4,7 @@
 
 export type ReactionMessageData = {
   +type: 19,
+  +localID?: string, // for optimistic creations. included by new clients
   +threadID: string,
   +creatorID: string,
   +time: number,
@@ -14,12 +15,13 @@
 
 export type RawReactionMessageInfo = {
   ...ReactionMessageData,
-  id: string,
+  id?: string, // null if local copy without ID yet
 };
 
 export type ReactionMessageInfo = {
   +type: 19,
-  +id: string,
+  +id?: string, // null if local copy without ID yet
+  +localID?: string, // for optimistic creations
   +threadID: string,
   +creator: RelativeUserInfo,
   +time: number,