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
ScrollToBottomButtonoverride key, and call theonPressthe 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
nullwhenshowNotificationisfalse.MessageFlashListkeeps the button mounted and passesfalsewhile 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:
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
Downicon from theiconsoverride 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, ormessageList.scrollToBottom.withCount.accessibilityLabelwith the count. The keys are exported asSCROLL_TO_BOTTOM_ACCESSIBILITY_LABEL_KEYandSCROLL_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. |
Related hooks
useChannel()anduseThreadContext()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. ItshasMoreHeadistruewhile the latest messages aren't loaded, andMessagePaginator.jumpToTheLatestMessage()loads them. See Message paginator.