Overview

The core of Stream Chat React Native is its flexibility. Many apps only need the theme. For deeper changes or new behavior, build a custom component.

Best Practices

  • Start with theming; only create custom components when behavior/layout must change.
  • Replace components via Channel props to keep integration consistent.
  • Return null to remove UI features cleanly.
  • Use the right context for your component’s scope; avoid message-level re-renders.
  • Keep custom components lightweight, especially inside MessageList.

When to use a custom component

Build a custom component when you need to insert custom UI, significantly change layout, or modify behavior. With few exceptions, what you see is what you get.

Exceptions

  • Padding, fonts, colors, borders, etc. can all be altered from the theme as styles are passed to most components and sub-components.
  • Messages can be aligned to one side using the forceAlignMessages prop on the Channel component.
  • Message content can be reordered using the messageContentOrder prop on Channel; but you may want to adjust the theming to account for changes to border alignments if you alter this order.

Changes beyond these tweaks require custom components. To remove a UI feature, replace it with a component that returns null.

How to use a custom component

Most custom components are passed as props on Channel (with a few exceptions on OverlayProvider, MessageList, and ChannelList).

To remove the fixed DateHeader, pass undefined:

<Channel
  ...
  DateHeader={undefined}
>

Your custom component replaces the default everywhere it is used. Most components read from context; some also receive props. DateHeader receives a dateString prop.

const MyNewComponent = ({ dateString }) => <Text>{`Hello World: ${dateString}`}</Text>;

<Channel
  ...
  DateHeader={MyNewComponent}
>

DateHeader

NoDateHeader

NewDateHeader

Using contexts

Custom components typically read from SDK contexts. Use the hooks within their provider scope. Some contexts, like MessageContext, are only available inside a Message, so you can't use them in DateHeader.

Example: replace DateHeader with a component that reads PaginatedMessageListContext and displays the last sender.

const MySenderComponent = () => {
  const { messages } = usePaginatedMessageListContext();
  const latestMessageSender = messages[messages.length - 1]?.user?.name;

  return <Text>{`Last Sender: ${latestMessageSender}`}</Text>;
};

<Channel
  ...
  DateHeader={MySenderComponent}
>

NameDateHeader

This lets you tailor both UI and the information you show.

Custom components within a Message should draw from mostly static contexts, except their own MessageContext. ThemeContext, MessagesContext, and TranslationContext are safe in most cases.

Memoizing values from contexts above the FlatList inside a row does not work well when those contexts update. Reading PaginatedMessageListContext inside each Message will cause the whole list to re-render.

Common customization patterns

Here are some frequently requested customization examples:

Custom message bubble

Replace the default message appearance with a custom design:

import { View, Text, StyleSheet } from "react-native";
import { Channel, useMessageContext } from "stream-chat-react-native";

const CustomMessageContent = () => {
  const { message, isMyMessage } = useMessageContext();

  return (
    <View
      style={[
        styles.messageContainer,
        isMyMessage ? styles.myMessage : styles.theirMessage,
      ]}
    >
      {!isMyMessage && (
        <Text style={styles.username}>{message.user?.name}</Text>
      )}
      <Text style={styles.messageText}>{message.text}</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  messageContainer: {
    padding: 12,
    borderRadius: 16,
    maxWidth: "80%",
    marginVertical: 4,
  },
  myMessage: {
    backgroundColor: "#007AFF",
    alignSelf: "flex-end",
  },
  theirMessage: {
    backgroundColor: "#E5E5EA",
    alignSelf: "flex-start",
  },
  username: {
    fontSize: 12,
    color: "#666",
    marginBottom: 4,
  },
  messageText: {
    fontSize: 16,
  },
});

// Usage
<Channel MessageContent={CustomMessageContent} />;

Custom send button

Create a send button with custom styling or behavior:

import { TouchableOpacity, StyleSheet } from "react-native";
import { useMessageInputContext, SendIcon } from "stream-chat-react-native";

const CustomSendButton = () => {
  const { sendMessage } = useMessageInputContext();

  return (
    <TouchableOpacity style={styles.sendButton} onPress={sendMessage}>
      <SendIcon />
    </TouchableOpacity>
  );
};

const styles = StyleSheet.create({
  sendButton: {
    backgroundColor: "#007AFF",
    borderRadius: 20,
    padding: 8,
    marginLeft: 8,
  },
});

// Usage
<Channel SendButton={CustomSendButton} />;

Remove a UI element

To completely remove a UI element, pass a component that returns null:

// Remove the typing indicator
<Channel TypingIndicator={() => null} />;

// Remove the date header
<Channel DateHeader={undefined} />;

// Remove message avatars
<Channel MessageAvatar={() => null} />;