renderText function

The renderText function is a core piece of functionality, which handles how the text of our message is going to be formatted/look like. The default renderText function parses a markdown string and outputs a ReactElement. Under the hood, the output is generated by the ReactMarkdown component from react-markdown library. The component transforms the markdown to ReactElement by using remark parser and remark and rehype plugins.

The default remark plugins used by SDK are:

  1. remark-gfm - a third party plugin to add GitHub-like markdown support

The default rehype plugins (both specific to this SDK) are:

  1. plugin to render user mentions
  2. plugin to render emojis

Overriding Defaults

Custom renderText Function

If you don’t want your chat implementation to support markdown syntax by default you can override the default behaviour by creating a custom renderText function which returns a React node and passing it down to the MessageList or MessageSimple component via renderText property.

For this particular example we’ll create a very primitive one which takes the message text passed down to it as a first argument and returns it wrapped in span element:

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

const customRenderText = (text) => {
  return <span>{text}</span>;
};

export const WrappedMessageList = () => (
  <MessageList renderText={customRenderText} />
);

Here’s also an example with VirtualizedMessageList which currently does not accept renderText directly:

import { VirtualizedMessageList, MessageSimple } from "stream-chat-react";

const customRenderText = (text) => {
  return <span>{text}</span>;
};

const CustomMessage = (props) => (
  <MessageSimple {...props} renderText={customRenderText} />
);

export const WrappedVirtualizedMessageList = () => (
  <VirtualizedMessageList Message={CustomMessage} />
);

Custom Element Rendering

If you feel like the default output is sufficient, but you’d like to adjust how certain ReactMarkdown components look like (like strong element generated by typing **strong**) you can do so by passing down options to a third argument of the default renderText function:

Types mention and emoji are special case component types generated by our SDK’s custom rehype plugins.

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

const CustomStrongComponent = ({ children }) => (
  <b className="custom-strong-class-name">{children}</b>
);

const CustomMentionComponent = ({ children, node: { mentionedUser } }) => (
  <a data-user-id={mentionedUser.id} href={`/user-profile/${mentionedUser.id}`}>
    {children}
  </a>
);

export const WrappedMessageList = () => (
  <MessageList
    renderText={(text, mentionedUsers) =>
      renderText(text, mentionedUsers, {
        customMarkDownRenderers: {
          strong: CustomStrongComponent,
          mention: CustomMentionComponent,
        },
      })
    }
  />
);

Custom Remark and Rehype Plugins

If you would like to extend the array of plugins used to parse the markdown, you can provide your own lists of remark resp. rehype plugins. The logic that determines what plugins are used and in which order can be specified in custom getRehypePlugins and getRemarkPlugins functions. These receive the default array of rehype and remark plugins for further customization. Both custom functions ought to be passed to the third renderText parameter. An example follows:

It is important to understand what constitutes a rehype or remark plugin. A good start is to learn about the library called react-remark which is used under the hood in our renderText function.

import { renderText, RenderTextPluginConfigurator } from "stream-chat-react";
import { customRehypePlugin } from "./rehypePlugins";
import { customRemarkPlugin } from "./remarkPlugins";

const getRehypePlugins: RenderTextPluginConfigurator = (plugins) => {
  return [customRehypePlugin, ...plugins];
};
const getRemarkPlugins: RenderTextPluginConfigurator = (plugins) => {
  return [customRemarkPlugin, ...plugins];
};

const customRenderText = (text, mentionedUsers) =>
  renderText(text, mentionedUsers, {
    getRehypePlugins,
    getRemarkPlugins,
  });

const WrappedMessageList = () => <MessageList renderText={customRenderText} />;

It is also possible to define your custom set of allowed tag names for the elements rendered from the parsed markdown. To perform the tree transformations, you will need to use libraries like unist-builder to build the trees and unist-util-visit or hast-util-find-and-replace to traverse the tree:

import { findAndReplace } from "hast-util-find-and-replace";
import { u } from "unist-builder";
import {
  defaultAllowedTagNames,
  renderText,
  RenderTextPluginConfigurator,
} from "stream-chat-react";

// wraps every letter b in <xxx></xxx> tags
const customTagName = "xxx";
const replace = (match) =>
  u("element", { tagName: customTagName }, [u("text", match)]);
const customRehypePlugin = () => (tree) => findAndReplace(tree, /b/, replace);

const getRehypePlugins: RenderTextPluginConfigurator = (plugins) => {
  return [customRehypePlugin, ...plugins];
};

const customRenderText = (text, mentionedUsers) =>
  renderText(text, mentionedUsers, {
    allowedTagNames: [...defaultAllowedTagNames, customTagName],
    getRehypePlugins,
  });

const WrappedMessageList = () => <MessageList renderText={customRenderText} />;
© Getstream.io, Inc. All Rights Reserved.