Info:
This is release candidate documentation for Stream Chat React SDK v15. For the latest stable version, see the latest version (v14).

Reaction components

The reaction components draw a message's reactions and let the user add them:

  • ReactionSelector: the quick reactions, opened from the smiley button next to a message or from Add reaction in its menu;
  • ReactionSelectorExtendedList: the full list, behind the selector's + button and the dialog's add button;
  • MessageReactions: the reactions on the message, with their counts;
  • MessageReactionsDetail: the dialog that lists who reacted.

Each is an override key, and reactionOptions sets the reactions they offer. Reactions explains how they work together, and how to change the reactions on offer, their style, their order and what a reaction does.

Best practices

  • Set reactionOptions once, around the whole app, so the selector, the message and the dialog show the same reactions.
  • Wrap a replacement for ReactionSelector in forwardRef. The key is typed as a forwardRef component, so a plain function doesn't type-check.
  • Declare sortReactions outside the component. A new comparator on every render sorts the reactions again on every render.

Customization

The SDK renders the ReactionSelector key in a dialog next to the message. From the smiley button it passes no props, from the menu only dialogId, and it never passes a ref. This replacement puts the default selector in a frame of its own:

import { forwardRef } from "react";
import type { ReactionSelectorProps } from "stream-chat-react";
import { ReactionSelector, WithComponents } from "stream-chat-react";

const FramedReactionSelector = forwardRef<
  HTMLDivElement,
  ReactionSelectorProps
>(function FramedReactionSelector(props, ref) {
  return (
    <div className="framed-reaction-selector" ref={ref}>
      <ReactionSelector {...props} />
    </div>
  );
});

const overrides = { ReactionSelector: FramedReactionSelector };

export const App = () => (
  <WithComponents overrides={overrides}>
    {/* the rest of the app */}
  </WithComponents>
);

Changing what a reaction does replaces the handler the same way.

ReactionSelector props

Prop Type Default Description
dialogId string ReactionSelector.getDialogId() The id of the dialog the selector closes after a pick.
handleReaction (reactionType: string, event: BaseSyntheticEvent) => Promise<void> handleReaction from the context Adds or removes a reaction. Used by the selector and its extended list.
own_reactions ReactionResponse[] message.own_reactions The user's reactions, which the selector shows as pressed.

The selector closes after a pick when the list's closeReactionSelectorOnClick is set. Its + button shows only when reactionOptions has extended reactions.

ReactionSelectorExtendedList takes the same props. It renders nothing without extended reactions. The default isn't exported by name; it's ReactionSelector.ExtendedList.

MessageReactions props

Prop Type Default Description
capLimit { clustered?: number; segmented?: number } { clustered: 5, segmented: 4 } How many reaction types show. Segmented reactions not at the top aren't capped.
flipHorizontalPosition boolean false Adds the str-chat__message-reactions--flipped-horizontally class, to anchor the reactions on the other side.
handleFetchReactions (reactionType?: string, sort?: SortParamRequest[]) => Promise<ReactionResponse[]> handleFetchReactions from the context Loads the users for the dialog.
own_reactions ReactionResponse[] message.own_reactions The user's reactions.
reaction_groups Record<string, ReactionGroupResponse> message.reaction_groups One summary per reaction type: its count and when it was first and last used. The reactions shown come from here.
reactionDetailsSort SortParamRequest[] reactionDetailsSort from the context The order of users in the dialog.
reactions ReactionResponse[] message.latest_reactions The recent reactions, used for the names of the users who reacted.
reverse boolean false Declared, but has no effect. The default MessageUI passes true.
sortReactions ReactionsComparator sortReactions from the context, else by first use Orders the reaction types.
verticalPosition 'top' | 'bottom' | null 'top' Adds the class that puts the reactions above or below the bubble. null adds neither.
visualStyle 'clustered' | 'segmented' | null 'clustered' clustered is one button with the total count; segmented is one button per type, with its count.

Only the reaction types in reactionOptions show, and a message with none of them renders nothing. Clicking the reactions opens MessageReactionsDetail, showing all reactions in the clustered style and the clicked type in the segmented one. A message with more than 1000 reactions doesn't open it.

ReactionsComparator isn't exported. Type a comparator as MessageListProps['sortReactions']; it compares two summaries with reactionType, reactionCount, firstReactionAt, lastReactionAt and isOwnReaction.

MessageReactionsDetail props

MessageReactions renders the dialog with every prop but dialogId, handleReaction, own_reactions and sort.

Prop Type Default Description
reactions ReactionSummary[] required The reaction types to list at the top, with their counts and emoji.
selectedReactionType string | null required The type whose users are listed. null lists every user, each with the emoji they picked.
handleFetchReactions (reactionType?: string, sort?: SortParamRequest[]) => Promise<ReactionResponse[]> handleFetchReactions from the context Loads the users, 25 at a time, up to 1000.
handleReaction (reactionType: string, event: BaseSyntheticEvent) => Promise<void> handleReaction from the context Used by the add step. Removing the user's own reaction always uses the context's handler.
onSelectedReactionTypeChange (reactionType: string | null) => void Called when the user picks a type, and with null when they pick it again.
own_reactions ReactionResponse[] message.own_reactions Passed to the extended list of the add step.
reactionDetailsSort SortParamRequest[] from the context, else [{ direction: -1, field: 'created_at' }] The order of users, sent with the query.
reactionGroups Record<string, ReactionGroupResponse> The summaries, used to go back to all users when the user removes the last reaction of the selected type.
sort SortParamRequest[] Declared, but not read. Use reactionDetailsSort.
totalReactionCount number Shown as "5 reactions" at the top. Without it, there's no count.
dialogId string Declared through the selector's props, but not read.

The add button shows only when reactionOptions has extended reactions, and opens ReactionSelectorExtendedList in the dialog. While users load, the dialog renders the LoadingIndicator key, or MessageReactionsDetailLoadingIndicator by default. That skeleton is exported for a dialog of your own.

Export What it is
reactionOptions key, ReactionOptions The reactions on offer: quick for the selector, extended for the full list. See The reactions on offer.
defaultReactionOptions The default set.
emojiToUnicode, unicodeToEmoji Convert between an emoji and the unicode code of a reaction option.
mapEmojiMartData(data) Turns the @emoji-mart/data set into extended reactions.
ReactionSelector.getDialogId({ messageId, threadList }), MessageReactionsDetail.getDialogId({ messageId }) The ids of the two dialogs, to open or close them through a dialog manager.

Hooks

  • useMessageContext() gives handleReaction, handleFetchReactions, sortReactions and reactionDetailsSort.
  • useReactionHandler(message) and useReactionsFetcher(message) build the context's two handlers, for a component outside a message. See Message hooks.
  • useComponentContext() returns reactionOptions and the reaction overrides in effect.