# renderText function

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

`renderText(text, mentionedUsers, options)` turns a message's text into React elements. The default renders Markdown with [`react-markdown`](https://github.com/remarkjs/react-markdown), turns plain URLs and e-mail addresses into links, and marks mentions and emoji. [renderText](https://getstream.io/chat/docs/sdk/react/v15/ui/components/message-list-ui/#rendertext) shows where it fits in a message.

By default it runs these plugins:

- **remark**, on the Markdown: `htmlToTextPlugin`, `keepLineBreaksPlugin`, [`remark-gfm`](https://github.com/remarkjs/remark-gfm) (with single tildes left as text), `plusPlusToEmphasis` and `imageToLink`;
- **rehype**, on the HTML tree: `emojiMarkdownPlugin`, and the mentions plugin when the message has mentions.

Only the tags in `defaultAllowedTagNames` render. Raw HTML in the text shows as text.

## Best practices

- Wrap the default `renderText` rather than replacing it, and pass on the `options` you receive: they carry the message's mentions.
- Declare your function outside the component. The default message UI doesn't re-render when only `renderText` changes.
- Keep `htmlToTextPlugin` when the text comes from users, so the HTML they type shows as text. Without it, that HTML is dropped from the message.

## Overriding defaults

Pass your function as `renderText` to `MessageList` or `VirtualizedMessageList`, and it renders the text of every message in the list. `MessageText`, `QuotedMessage` and `StreamedMessageText` take a `renderText` prop too, for a custom message UI.

```ts
type RenderTextFunction = (
  text?: string,
  /** Deprecated: use `options.messageMentionEntities`. */
  mentionedUsers?: UserResponse[],
  options?: RenderTextOptions,
) => ReactNode;
```

| Option                    | Type                                                                    | Description                                                                                                       |
| ------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `allowedTagNames`         | `string[]`                                                              | The tags that render. Defaults to `defaultAllowedTagNames`. Other tags are dropped and their content kept.        |
| `customMarkDownRenderers` | `Options['components']` of `react-markdown`, plus `emoji` and `mention` | Components that render a tag, such as `strong` or `a`. The SDK renders `a`, `emoji` and `mention` itself.         |
| `getRehypePlugins`        | `RenderTextPluginConfigurator`                                          | Receives the default rehype plugins and returns the list to use.                                                  |
| `getRemarkPlugins`        | `RenderTextPluginConfigurator`                                          | Receives the default remark plugins and returns the list to use.                                                  |
| `messageMentionEntities`  | `RenderTextMentionEntity[]`                                             | The mentions to mark: users, `@channel`, `@here`, roles and user groups. `MessageText` fills it from the message. |

`RenderTextPluginConfigurator` is `(defaultPlugins: PluggableList) => PluggableList`.

### Custom renderText function

A function of your own replaces all of it. This one shows the text as it is, with no Markdown, links, mentions or emoji marks:

```tsx
import type { RenderTextFunction } from "stream-chat-react";
import { MessageList } from "stream-chat-react";

const renderPlainText: RenderTextFunction = (text) => <span>{text}</span>;

export const PlainTextMessageList = () => (
  <MessageList renderText={renderPlainText} />
);
```

### Custom element rendering

To keep the default output and change how one kind of element renders, pass components in `customMarkDownRenderers`. `mention` and `emoji` are elements the SDK's rehype plugins create. A mention component receives the mention in `node.mentionedEntity`; for a user mention, `node.mentionedUser` holds the same user and is deprecated.

This list renders bold text with `<b>` and links user mentions to a profile page:

```tsx
import type { PropsWithChildren } from "react";
import type { MentionProps, RenderTextFunction } from "stream-chat-react";
import { MessageList, renderText } from "stream-chat-react";

const Bold = ({ children }: PropsWithChildren) => (
  <b className="message-bold">{children}</b>
);

const ProfileMention = ({ children, node }: MentionProps) => {
  const { mentionedEntity } = node;

  if (mentionedEntity.mentionType !== "user") {
    return (
      <span data-mention-type={mentionedEntity.mentionType}>{children}</span>
    );
  }

  return <a href={`/users/${mentionedEntity.id}`}>{children}</a>;
};

const renderTextWithProfiles: RenderTextFunction = (
  text,
  mentionedUsers,
  options,
) =>
  renderText(text, mentionedUsers, {
    ...options,
    customMarkDownRenderers: {
      ...options?.customMarkDownRenderers,
      mention: ProfileMention,
      strong: Bold,
    },
  });

export const MessagesWithProfiles = () => (
  <MessageList renderText={renderTextWithProfiles} />
);
```

### Styling mentions per type

To style each kind of mention differently, keep the default `mention` component and use CSS. It renders a `<span>` with the mention's type and id:

```html
<span
  class="str-chat__message-mention"
  data-mention-type="user | channel | here | role | user_group"
  data-mention-id="<entity id>"
  data-user-id="<user id, only for user mentions>"
  >@Support Team</span
>
```

```css
.str-chat__message-mention[data-mention-type="channel"],
.str-chat__message-mention[data-mention-type="here"] {
  color: #e62e2e;
}
.str-chat__message-mention[data-mention-type="role"] {
  color: #9c27b0;
}
.str-chat__message-mention[data-mention-type="user_group"] {
  color: #0c8d3a;
}
```

[Mentions](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/mentions/) explains where each kind of mention comes from.

### Custom remark and rehype plugins

`getRehypePlugins` and `getRemarkPlugins` receive the default plugins and return the list to use, so you can add to it, reorder it or replace it. A plugin that creates a tag the defaults don't allow needs the tag in `allowedTagNames` too.

This one wraps every "urgent" in a `<mark>`:

```tsx
import type { Nodes } from "hast";
import type { ReplaceFunction } from "hast-util-find-and-replace";
import { findAndReplace } from "hast-util-find-and-replace";
import type {
  RenderTextFunction,
  RenderTextPluginConfigurator,
} from "stream-chat-react";
import {
  defaultAllowedTagNames,
  MessageList,
  renderText,
} from "stream-chat-react";
import { u } from "unist-builder";

const wrapInMark: ReplaceFunction = (match) =>
  u("element", { properties: {}, tagName: "mark" }, [u("text", match)]);

const markUrgent = () => (tree: Nodes) =>
  findAndReplace(tree, [/\burgent\b/gi, wrapInMark]);

const getRehypePlugins: RenderTextPluginConfigurator = (plugins) => [
  ...plugins,
  markUrgent,
];

const renderTextWithMarks: RenderTextFunction = (
  text,
  mentionedUsers,
  options,
) =>
  renderText(text, mentionedUsers, {
    ...options,
    allowedTagNames: [...defaultAllowedTagNames, "mark"],
    getRehypePlugins,
  });

export const MessagesWithMarks = () => (
  <MessageList renderText={renderTextWithMarks} />
);
```

The example uses [`hast-util-find-and-replace`](https://github.com/syntax-tree/hast-util-find-and-replace) and [`unist-builder`](https://github.com/syntax-tree/unist-builder), as the SDK's own plugins do. Install them in your app to use them. The SDK uses `hast-util-find-and-replace` 5, and the `findAndReplace(tree, [find, replace])` call above is that version's.

### Existing remark and rehype plugins

`stream-chat-react` exports the plugins it uses, and a few more:

| Plugin                                    | Kind   | Default | What it does                                                                                                                                           |
| ----------------------------------------- | ------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `htmlToTextPlugin`                        | remark | yes     | Shows HTML in the Markdown as text, so none of it renders.                                                                                             |
| `keepLineBreaksPlugin`                    | remark | yes     | Turns each blank line beyond the first between two blocks into a `<br>`.                                                                               |
| `plusPlusToEmphasis`                      | remark | yes     | Renders `++text++` as `<ins>text</ins>`, except in code and links.                                                                                     |
| `imageToLink`                             | remark | yes     | Renders a Markdown image as a link. Pass `[imageToLink, { getTextLabelFrom }]` with `'url'` (the default), `'alt'` or `'title'` to pick the link text. |
| `remarkIgnoreMarkdown`                    | remark | no      | Shows the whole source as one paragraph of plain text. Plain URLs then show as Markdown links, because `renderText` rewrites them first.               |
| `emojiMarkdownPlugin`                     | rehype | yes     | Wraps each emoji in an `emoji` element.                                                                                                                |
| `mentionsMarkdownPlugin(mentionEntities)` | rehype | yes     | Wraps each mention's display text in a `mention` element that carries the mention. The default adds it only when the message has mentions.             |

To pick the plugins yourself, return your own list. This one keeps only `keepLineBreaksPlugin` and no rehype plugin, so GitHub Markdown, the other remark defaults, mentions and emoji are all off:

```tsx
import type { RenderTextFunction } from "stream-chat-react";
import {
  keepLineBreaksPlugin,
  MessageList,
  renderText,
} from "stream-chat-react";

const renderTextWithLineBreaksOnly: RenderTextFunction = (
  text,
  mentionedUsers,
  options,
) =>
  renderText(text, mentionedUsers, {
    ...options,
    getRehypePlugins: () => [],
    getRemarkPlugins: () => [keepLineBreaksPlugin],
  });

export const MessagesWithLineBreaksOnly = () => (
  <MessageList renderText={renderTextWithLineBreaksOnly} />
);
```

### Mentions in your own text

`MessageText` passes the message's mentions in `options.messageMentionEntities`. When you call `renderText` for other text, pass the mentions to mark yourself. The second argument, `mentionedUsers`, covers user mentions only and is deprecated:

```tsx
import type { RenderTextFunction } from "stream-chat-react";
import { renderText } from "stream-chat-react";

export const renderAnnouncement: RenderTextFunction = (
  text,
  mentionedUsers,
  options,
) =>
  renderText(text, mentionedUsers, {
    ...options,
    messageMentionEntities: [
      ...(options?.messageMentionEntities ?? []),
      { id: "channel", mentionType: "channel", name: "channel" },
      { id: "admin", mentionType: "role", name: "admin" },
    ],
  });
```

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/component-reference/message/rendertext-function/](https://getstream.io/chat/docs/sdk/react/v15/component-reference/message/rendertext-function/).