# Message UI

The Message UI component is a core building block of the chat experience. Designing it well is tricky, so the SDK includes a pre-built [`MessageSimple`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageSimple.tsx) component that’s easy to customize via [CSS variables](/chat/docs/sdk/react/v13/theming/component-variables/) or component overrides ([`ComponentContext`](/chat/docs/sdk/react/v13/components/contexts/component_context#message/)).

## Best Practices

- Start by theming `MessageSimple` before building a custom Message UI component.
- Use `MessageText` to preserve markdown, links, and mentions.
- Keep custom message layout consistent with thread and list views.
- Access message data via `useMessageContext` instead of prop drilling.
- Test the custom Message UI component with all message types (system, deleted, attachments).

In this guide, we’ll build a simplified custom Message UI component using both pre-built and custom components.

### Message Text and Avatars

Start with the simplest Message UI component: render raw text.

```tsx
import { useMessageContext, Channel } from "stream-chat-react";

const CustomMessageUi = () => {
  const { message } = useMessageContext();

  return <div data-message-id={message.id}>{message.text}</div>;
};
```

<admonition type="note">

The Message UI component and its children can access [`MessageContext`](/chat/docs/sdk/react/v13/components/contexts/message_context/) via `useMessageContext` for message data and handlers.

</admonition>

To see the changes, pass this component to `Channel` or `MessageList`/`VirtualizedMessageList` via the `Message` prop.

```tsx
<Channel Message={CustomMessageUi}>...</Channel>
```

![](@chat-sdk/react/v13/_assets/custom-message-ui-0.png)

All messages now appear on one side and we can’t tell who sent them. Let’s fix that with CSS and render the sender name via `message.user`.

Our message will be on the right and the message of the other senders will be on the left side of the screen.

<Tabs>

</Tabs>

![](@chat-sdk/react/v13/_assets/custom-message-ui-1.png)

This already looks better, but we can do more. Let’s switch to avatars using the pre-built [`Avatar`](/chat/docs/sdk/react/v13/components/utility-components/avatar/) component.

<Tabs>

</Tabs>

![](@chat-sdk/react/v13/_assets/custom-message-ui-2.png)

Our UI looks good, but what about complex text (links, mentions, markdown)? Right now it’s plain text and not interactive.

![](@chat-sdk/react/v13/_assets/custom-message-ui-3a.png)

Let’s improve this by using [`MessageText`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageText.tsx), which uses [`renderText`](/chat/docs/sdk/react/v13/components/message-components/render-text/) to turn links, mentions, and Markdown into interactive elements.

<Tabs>

</Tabs>

![](@chat-sdk/react/v13/_assets/custom-message-ui-3b.png)

<admonition type="note">

Mention highlights don’t include a default click handler. See the [Mentions Actions guide](/chat/docs/sdk/react/v13/guides/theming/actions/mentions_actions/) for more.

</admonition>

### Metadata

So far we’ve covered avatars and text rendering, but the UI still feels sparse. Let’s add creation date, “edited” status, and delivery/read indicators.

<Tabs>

</Tabs>

![](@chat-sdk/react/v13/_assets/custom-message-ui-4.png)

Message grouping is handled by the SDK. Each message wrapper gets a class, so you can show metadata only when it makes sense and keep the UI clean.

```css
.custom-message-ui__metadata {
  /* removed-line */
  display: flex;
  /* added-line */
  display: none;
  font-size: x-small;
  align-items: baseline;
}

/* added-block-start */
.str-chat__li--bottom .custom-message-ui__metadata,
.str-chat__li--single .custom-message-ui__metadata {
  display: flex;
}
/* added-block-end */
```

![](@chat-sdk/react/v13/_assets/custom-message-ui-5.png)

<admonition type="note">

You can utilize [`MessageContext`](/chat/docs/sdk/react/v13/components/contexts/message_context/) properties [`firstOfGroup`](/chat/docs/sdk/react/v13/components/contexts/message_context#firstofgroup/), [`endOfGroup`](/chat/docs/sdk/react/v13/components/contexts/message_context#endofgroup/), and [`groupedByUser`](/chat/docs/sdk/react/v13/components/contexts/message_context#groupedbyuser/) if you use [`VirtualizedMessageList`](/chat/docs/sdk/react/v13/components/core-components/virtualized_list/) for conditional metadata rendering. These properties **are not available** in regular [`MessageList`](/chat/docs/sdk/react/v13/components/core-components/message_list/).

</admonition>

The SDK also provides [`MessageStatus`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageStatus.tsx) and [`MessageTimestamp`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageTimestamp.tsx). They include extra logic and cover most use cases, so we won’t replace them here.

### Message Actions

So far we’ve focused on presentation. Beyond mentions and links, there’s little interactivity. Next, we’ll add message actions (delete, reply, pin, etc.) and reactions.

[`MessageContext`](/chat/docs/sdk/react/v13/components/contexts/message_context/) provides message data and handlers. To implement a subset of actions, use `handleDelete`, `handlePin`, `handleFlag`, and `handleThread` and wire them to your UI buttons.

<Tabs>

</Tabs>

![](@chat-sdk/react/v13/_assets/custom-message-ui-6.png)

Now that actions are enabled, we also need UI that reflects message state. We already handle the `"pinned"` state via `message.pinned`. See the [Pin Indicator](/chat/docs/sdk/react/v13/guides/customization/pin_indicator/) guide for more options. Let’s cover a few remaining pieces.

<admonition type="note">

The following code samples contain only the code related to the appropriate components, if you're following along you can copy and add the following examples to whatever you have created up until now. The whole example is [at the bottom](/chat/docs/sdk/react/v13/guides/theming/message_ui#complete-example/) of this guide.

</admonition>

#### Thread and Reply Count

First - upon opening a thread and replying to a message, the message's property `reply_count` changes; let's add the count indicator button beside the rest of the metadata elements so the end users can access the thread from two places.

<Tabs>

</Tabs>

#### Deleted Message

If you allow soft delete, render a different UI for deleted messages. Check `message.deleted_at` and fall back to “message deleted” text.

```tsx
const CustomMessageUi = () => {
  const { isMyMessage, message } = useMessageContext();

  const messageUiClassNames = ["custom-message-ui"];

  if (isMyMessage()) {
    messageUiClassNames.push("custom-message-ui--mine");
  } else {
    messageUiClassNames.push("custom-message-ui--other");
  }

  return (
    <div className={messageUiClassNames.join(" ")} data-message-id={message.id}>
      // added-block-start
      {message.deleted_at && (
        <div className="custom-message-ui__body">
          This message has been deleted...
        </div>
      )}
      {!message.deleted_at && (
        <>
          <div className="custom-message-ui__body">
            <Avatar
              image={message.user?.image}
              name={message.user?.name || message.user?.id}
            />
            <MessageText />
          </div>
          <CustomMessageUiMetadata />
          <CustomMessageUiActions />
        </>
      )}
      // added-block-end
    </div>
  );
};
```

![](@chat-sdk/react/v13/_assets/custom-message-ui-7.png)

### Reactions

With message actions in place, the UI is more interactive but still incomplete. Next we’ll add a simple reactions selector (thumbs up/down) by reusing [`ReactionsList`](/chat/docs/sdk/react/v13/components/message-components/reactions#reactionslist-props/). Start by defining `customReactionOptions` (see [Reactions Customization](/chat/docs/sdk/react/v13/guides/theming/reactions/)) and passing them to `reactionOptions` on [`Channel`](/chat/docs/sdk/react/v13/components/core-components/channel/).

```tsx
const customReactionOptions = [
  { name: "Thumbs up", type: "+1", Component: () => <>👍</> },
  { name: "Thumbs down", type: "-1", Component: () => <>👎</> },
];

<Channel Message={CustomMessageUi} reactionOptions={customReactionOptions}>
  ...
</Channel>;
```

And now that's done we can continue by extending our `CustomMessageUiActions` component using these newly defined options.

```tsx
const CustomMessageUiActions = () => {
  const {
    handleDelete,
    handleFlag,
    handleOpenThread,
    handlePin,
    // added-line
    handleReaction,
    message,
    threadList,
  } = useMessageContext();

  // added-line
  const { reactionOptions } = useComponentContext();

  if (threadList) return null;

  return (
    <div className="custom-message-ui__actions">
      <div className="custom-message-ui__actions-group">
        <button onClick={handlePin} title={message.pinned ? "Unpin" : "Pin"}>
          {message.pinned ? "📍" : "📌"}
        </button>
        <button onClick={handleDelete} title="Delete">
          🗑️
        </button>
        <button onClick={handleOpenThread} title="Open thread">
          ↩️
        </button>
        <button onClick={handleFlag} title="Flag message">
          🚩
        </button>
      </div>
      // added-block-start
      <div className="custom-message-ui__actions-group">
        {reactionOptions.map(({ Component, name, type }) => (
          <button
            key={type}
            onClick={(e) => handleReaction(type, e)}
            title={`React with: ${name}`}
          >
            <Component />
          </button>
        ))}
      </div>
      // added-block-end
    </div>
  );
};
```

Finally, we can add the `ReactionsList` component to our `CustomMessageUi` component and adjust the styles accordingly.

#### Complete Example

<Tabs>

</Tabs>

![](@chat-sdk/react/v13/_assets/custom-message-ui-8.png)

### Attachments

The topic of attachments is pretty substantial by itself, so we won't be covering it in this guide. Please, refer to the source code of our default [`MessageSimple`](https://github.com/GetStream/stream-chat-react/blob/fdd021880d783af39e46e1327f2eb47c19199f09/src/components/Message/MessageSimple.tsx#L196-L198) for details on implementation and see the [Custom Attachment](/chat/docs/sdk/react/v13/guides/theming/actions/attachment_actions#custom-attachment/) guide for more customization options.

### Read More

Functionalities relevant to the Message UI component that are also not covered in this guide:

- [Edit Message](https://github.com/GetStream/stream-chat-react/blob/fdd021880d783af39e46e1327f2eb47c19199f09/src/components/Message/MessageSimple.tsx#L150-L161) functionality
- [Message Bounced](https://github.com/GetStream/stream-chat-react/blob/fdd021880d783af39e46e1327f2eb47c19199f09/src/components/Message/MessageSimple.tsx#L162-L168) functionality
- [Permissions](/chat/docs/sdk/react/v13/components/contexts/channel_state_context#channelcapabilities/) of the message actions


---

This page was last updated at 2026-04-21T09:53:42.154Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v13/guides/theming/message_ui/](https://getstream.io/chat/docs/sdk/react/v13/guides/theming/message_ui/).