This is beta documentation for Stream Chat React SDK v14. For the latest stable version, see the latest version (v13) .

Reactions

The SDK includes built-in message reactions. The main reaction surfaces are:

Best Practices

  • Keep reaction options concise so the selector stays scannable.
  • Memoize custom sortReactions comparators.
  • Build a custom compact reaction UI around MessageReactions only when the default clustered or segmented styles are not enough.
  • Customize the selector and detail dialog together so their emoji sets stay aligned.
  • If you replace the clustered reactions trigger, preserve its dialog-state ARIA attributes such as aria-expanded and aria-pressed.
  • Use reaction_groups for grouped reaction summaries.

Sorting Reactions

By default, reactions are sorted chronologically by the first time a reaction type was used. You can change this with sortReactions on MessageList or VirtualizedMessageList.

import { Channel, MessageComposer, MessageList } from "stream-chat-react";

const sortByReactionCount = (a, b) => b.reactionCount - a.reactionCount;

const App = () => (
  <Channel>
    <MessageList sortReactions={sortByReactionCount} />
    <MessageComposer />
  </Channel>
);

Pass reactionDetailsSort to MessageList, VirtualizedMessageList, Message, or MessageReactions to control the server-side ordering of users in the default reaction-detail dialog.

Customization

Use WithComponents to replace the default reaction surfaces for a Channel subtree:

import {
  Channel,
  ChannelHeader,
  MessageComposer,
  MessageList,
  MessageReactions,
  MessageReactionsDetail,
  ReactionSelector,
  Thread,
  Window,
  WithComponents,
} from "stream-chat-react";

const CustomReactionSelector = (props) => <ReactionSelector {...props} />;
const CustomMessageReactions = (props) => (
  <MessageReactions {...props} visualStyle="segmented" />
);
const CustomMessageReactionsDetail = (props) => (
  <MessageReactionsDetail {...props} />
);

const App = () => (
  <WithComponents
    overrides={{
      MessageReactionsDetail: CustomMessageReactionsDetail,
      MessageReactions: CustomMessageReactions,
      ReactionSelector: CustomReactionSelector,
    }}
  >
    <Channel>
      <Window>
        <ChannelHeader />
        <MessageList />
        <MessageComposer />
      </Window>
      <Thread />
    </Channel>
  </WithComponents>
);

Detail Dialog Behavior

MessageReactionsDetail now treats selectedReactionType={null} as the "show all reactions" state.

  • clicking the currently selected reaction type again clears the filter and returns to the all-reactions view
  • when no reaction type is selected, each user row can show the emoji for the reaction that user sent
  • the clustered MessageReactions trigger reflects dialog state with aria-expanded and aria-pressed

If you rebuild the detail view from scratch, preserve that null filter state instead of treating it as "nothing selected".

Loading State

The SDK exports MessageReactionsDetailLoadingIndicator for the default reactions-detail skeleton. Use it directly in custom detail UIs when you want the same loading appearance:

import { MessageReactionsDetailLoadingIndicator } from "stream-chat-react";

const CustomReactionsDetailLoadingState = () => (
  <MessageReactionsDetailLoadingIndicator />
);

Positioning

Use verticalPosition to control whether MessageReactions renders above or below the message bubble. The SDK defaults to verticalPosition="top".

import { MessageReactions } from "stream-chat-react";

const CustomMessageReactions = (props) => (
  <MessageReactions
    {...props}
    verticalPosition="bottom"
    visualStyle="segmented"
  />
);

verticalPosition works together with the existing layout props:

  • use verticalPosition="bottom" to move the reactions list below the message bubble
  • keep verticalPosition="top" for the default layout
  • use flipHorizontalPosition when you want to change the horizontal anchoring relative to the message alignment
  • set verticalPosition={null} only when you want to remove the default top/bottom modifier class and fully control positioning in a custom wrapper

One implementation detail worth knowing: in segmented mode, the SDK only applies the built-in 4-reaction cap and overflow counter when verticalPosition="top". If you move segmented reactions to the bottom, the full processed list is rendered instead of the top-position capped variant.

ReactionSelector Props

PropDescriptionType
handleReactionFunction that adds or removes a reaction. Overrides the value from MessageContext.(reactionType: string, event: React.BaseSyntheticEvent) => Promise<void>
own_reactionsOwn reactions used to highlight the selected state.ReactionResponse[]

MessageReactions Props

PropDescriptionType
flipHorizontalPositionControls whether the horizontal position is flipped relative to the current message alignment. Defaults to false.boolean
handleFetchReactionsCustom loader for reaction details.MessageContextValue["handleFetchReactions"]
own_reactionsOwn reactions used to highlight the current user's reactions.ReactionResponse[]
reaction_groupsGrouped reaction summary used to build the list.Record<string, ReactionGroupResponse>
reactionDetailsSortSort options used when loading reaction details for MessageReactionsDetail.MessageContextValue["reactionDetailsSort"]
reactionsRaw reaction objects used to build the grouped list.ReactionResponse[]
reverseDisplays reactions in reverse order. Defaults to false.boolean
sortReactionsComparator used to order grouped reactions.ReactionsComparator
verticalPositionControls whether the list renders above or below the message bubble. Defaults to 'top'.'top' | 'bottom' | null
visualStyleControls whether the list renders in clustered or segmented mode. Defaults to 'clustered'.'clustered' | 'segmented' | null

If you need a denser inline reactions presentation than the default clustered or segmented modes, build a custom component around the current reaction data and register it through WithComponents overrides={{ MessageReactions: CustomMessageReactions }}.

MessageReactionsDetail Props

PropDescriptionType
handleFetchReactionsCustom loader for fetching reaction details.MessageContextValue["handleFetchReactions"]
onSelectedReactionTypeChangeCallback used when the selected reaction type changes.(reactionType: ReactionType | null) => void
reactionGroupsGrouped reaction summary used to keep the detail view in sync when reactions are removed.Record<string, ReactionGroupResponse>
reactionDetailsSortSort options used to fetch reaction details.MessageContextValue["reactionDetailsSort"]
reactionsGrouped reaction summary used to build the detail UI.ReactionSummary[]
selectedReactionTypeCurrently selected reaction type in the detail view. Use null for the "show all reactions" state.ReactionType | null
totalReactionCountTotal number of reactions shown in the detail dialog.number