---
id: reactions
title: Reactions Customization
---

import ImageShowcase from '@site/src/components/ImageShowcase';
import ReactionSelectorWithCustomOptions from '../../assets/reaction-selector-w-custom-options.png';
import ReactionsListWithCustomOptions from '../../assets/reactions-list-w-custom-options.png';

In this example, we'll demonstrate how to override the SDK's default reaction set, which is exported as [`defaultReactionOptions`](https://github.com/GetStream/stream-chat-react/blob/v11.0.0/src/components/Reactions/reactionOptions.tsx) variable. We'll replace the default set with up and down arrows, simulating an up/down voting feature.

Under the hood, our [`ReactionSelector`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Reactions/ReactionSelector.tsx), [`ReactionsList`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Reactions/ReactionsList.tsx), [`SimpleReactionsList`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Reactions/SimpleReactionsList.tsx) and [`ReactionsListModal`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Reactions/ReactionsListModal.tsx) components render individual emoji components defined in the `reactionOptions`. Therefore, the array with your custom reactions needs to conform to `ReactionOptions` type:

```tsx
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:

```tsx
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>
);
```

:::note
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`):

```tsx
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>
);
```

:::note
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.
:::

<ImageShowcase
  items={[
    {
      image: ReactionSelectorWithCustomOptions,
      caption: 'Custom reactionOptions rendered through ReactionSelector',
      alt: 'Custom reactionOptions rendered through ReactionSelector',
    },
    {
      image: ReactionsListWithCustomOptions,
      caption: 'Custom reactionOptions rendered through ReactionsList',
      alt: '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:

```tsx
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_](../../release-guides/upgrade-to-v11.mdx#introducing-new-reactions) section of our _Upgrade to v11_ guide.
