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.useOwnUnreadCount(channel)returns the current user's unread count in a channel, kept current as reads arrive, orundefinedwithout a channel. The default button's badge uses it.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.