Reactions Customization

In this example, we’ll demonstrate how to override the SDK’s default reaction set, which is exported as defaultReactionOptions variable. We’ll replace the default set with up and down arrows, simulating an up/down voting feature.

Under the hood, our ReactionSelector, ReactionsList, SimpleReactionsList and ReactionsListModal components render individual emoji components defined in the reactionOptions. Therefore, the array with your custom reactions needs to conform to ReactionOptions type:

type ReactionOptions = Array<{
  type: string;
  Component: React.ComponentType;
  name?: string;
}>;

Let’s construct a simple option list consisting of 'arrow-up' and 'arrow-down' native emojis. To override the default set we’ll need to pass this option list to the component context through Channel component so that our default components can pick it up:

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

const customReactionOptions = [
  {
    type: "arrow_up",
    Component: () => <>⬆️</>,
    name: "Upwards Black Arrow",
  },
  {
    type: "arrow_down",
    Component: () => <>⬇️</>,
    name: "Downwards Black Arrow",
  },
];

export const WrappedChannel = ({ children }) => (
  <Channel reactionOptions={customReactionOptions}>{children}</Channel>
);

Please note that types missing from the option list won’t be registered and might lead to sub-optimal user experience.

If you need to, you can pass these options to each of the default components individually (component props are prioritized even when the reactionOptions are routed through the Channel):

import { Channel, ReactionsList, ReactionSelector } from "stream-chat-react";

const CustomReactionsList = (props) => (
  <ReactionsList {...props} reactionOptions={customReactionOptions} />
);

// ReactionSelector component requires forwarded reference
const CustomReactionSelector = forwardRef((props, ref) => (
  <ReactionSelector
    {...props}
    ref={ref}
    reactionOptions={selectorReactionOptions}
  />
));

export const WrappedChannel = ({ children }) => (
  <Channel
    ReactionsList={CustomReactionsList}
    ReactionSelector={CustomReactionSelector}
  >
    {children}
  </Channel>
);

While ReactionSelector can display a subset of available reactions (to limit what certain users can react with), the ReactionsList should have the whole list available if applicable.

Custom reactionOptions rendered through ReactionSelector

Custom reactionOptions rendered through ReactionsList

Custom Reaction Handler

If you need to adjust the default behavior you can certainly do so by replacing reaction handler while keeping the default component intact:

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

const CustomReactionSelector = React.forwardRef((props, ref) => {
  const {
    message: { own_reactions: ownReactions = [], id: messageId },
  } = useMessageContext("CustomReactionSelector");
  const { channel } = useChannelStateContext("CustomReactionSelector");

  const handleReaction = useCallback(
    async (reactionType, event) => {
      // your custom logic with default behavior (minus optimistic updates)

      console.log({ event });

      const hasReactedWithType =
        (ownReactions ?? []).some(
          (reaction) => reaction.type === reactionType,
        ) ?? false;

      if (hasReactedWithType) {
        await channel.deleteReaction(messageId, reactionType);
        return;
      }

      await channel.sendReaction(messageId, { type: reactionType });
    },
    [channel, ownReactions, messageId],
  );

  return (
    <ReactionSelector {...props} handleReaction={handleReaction} ref={ref} />
  );
});

// and then just add it to the component context
export const WrappedChannel = ({ children }) => (
  <Channel ReactionSelector={CustomReactionSelector}>{children}</Channel>
);

Read More

See more on customization options in Introducing new reactions section of our Upgrade to v11 guide.

© Getstream.io, Inc. All Rights Reserved.