Info:
This is release candidate documentation for Stream Chat React Native SDK v10. For the latest stable version, see the latest version (v9).

ScrollToBottomButton

ScrollToBottomButton is the round button that floats over the bottom of the message list while the user isn't at the latest message. In a channel, its badge shows the user's unread count. MessageList and MessageFlashList render it through the ScrollToBottomButton override key and pass it what a press does. Floating controls explains when it shows.

Best practices

  • Replace it with the ScrollToBottomButton override key, and call the onPress the list passes. The list decides what a press does: scroll down, or jump to the latest messages when they aren't loaded, then mark the channel read.
  • Return null when showNotification is false. MessageFlashList keeps the button mounted and passes false while it should be hidden.
  • Read the unread count from the channel's read state, as the default does, rather than counting messages yourself.

Basic usage

This replacement shows a text button with the unread count:

src/JumpToLatestButton.tsx
import { Pressable, StyleSheet, Text } from "react-native";
import type { ReadState } from "stream-chat";
import {
  type ScrollToBottomButtonProps,
  useChannel,
  useChatContext,
  useStateStore,
  useThreadContext,
  WithComponents,
} from "stream-chat-react-native";

const selectRead = (state: ReadState) => ({ read: state.read });

const JumpToLatestButton = ({
  onPress,
  showNotification = true,
}: ScrollToBottomButtonProps) => {
  const { client } = useChatContext();
  const channel = useChannel();
  const { thread } = useThreadContext();
  const { read } = useStateStore(channel.state, selectRead);
  const unreadCount = client.userId
    ? (read[client.userId]?.unread_messages ?? 0)
    : 0;

  if (!showNotification) {
    return null;
  }

  return (
    <Pressable
      accessibilityRole="button"
      onPress={onPress}
      style={styles.button}
    >
      <Text style={styles.label}>
        {!thread && unreadCount ? `${unreadCount} new` : "Latest"}
      </Text>
    </Pressable>
  );
};

const overrides = { ScrollToBottomButton: JumpToLatestButton };

export const ListWithJumpButton = ({
  children,
}: {
  children: React.ReactNode;
}) => <WithComponents overrides={overrides}>{children}</WithComponents>;

const styles = StyleSheet.create({
  button: {
    backgroundColor: "#005fff",
    borderRadius: 16,
    paddingHorizontal: 12,
    paddingVertical: 6,
  },
  label: { color: "white", fontWeight: "600" },
});

The list positions the button absolutely, 16 points from the right, and above the composer when the composer floats. Change the position through theme.messageList.scrollToBottomButtonContainer.

What the default renders

  • An outlined icon button with the Down icon from the icons override map.
  • In a channel, a badge with the user's unread count, from channel.state's read state, while the count is above zero. In a thread, no badge.
  • Its accessibility label is messageList.scrollToBottom.accessibilityLabel, or messageList.scrollToBottom.withCount.accessibilityLabel with the count. The keys are exported as SCROLL_TO_BOTTOM_ACCESSIBILITY_LABEL_KEY and SCROLL_TO_BOTTOM_WITH_COUNT_ACCESSIBILITY_LABEL_KEY.

With accessibility turned on, the list also gets a scroll-to-bottom accessibility action while the button shows, so screen reader users reach the same jump without finding the button.

Props

Prop Type Default Description
onPress () => void required Called when the user presses the button. The list passes a function that scrolls to the newest message, or jumps to the latest messages when they aren't loaded, hides the button, and in a channel marks the channel read with updateChannelUnreadState: false, so the unread separator stays where it was.
showNotification boolean true Whether the button renders. MessageList mounts the button only while it should show; MessageFlashList mounts it always and passes the visibility here.
  • useChannel() and useThreadContext() tell the button whether it's in a thread.
  • useStateStore(channel.state, selector) reads the channel's read state, as in the sample above.
  • useMessagePaginator() returns the paginator the list renders. Its hasMoreHead is true while the latest messages aren't loaded, and MessagePaginator.jumpToTheLatestMessage() loads them. See Message paginator.