# Message history The history feature enables users to retrieve messages that have been previously sent in a room. [Message persistence](https://ably.com/docs/chat/rooms.md?source=llms.txt#persistence) is enabled by default for all chat rooms, with messages stored for 30 days. You can extend this retention period up to 365 days by [contacting us](https://ably.com/support). ## Retrieve previously sent messages Use the [`messages.history()`](https://ably.com/docs/chat/api/javascript/messages.md?source=llms.txt#history)[`messages.history()`](https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/history(withparams:))[`messages.history()`](https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat/com.ably.chat/-messages/history.html) method to retrieve messages that have been previously sent to a room. This returns a paginated response, which can be queried further to retrieve the next set of messages. Use the [`history()`](https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-react.UseMessagesResponse.html#history) method available from the response of the `useMessages` hook to retrieve messages that have been previously sent to a room. This returns a paginated response, which can be queried further to retrieve the next set of messages. ### Javascript ``` const historicalMessages = await room.messages.history({ orderBy: OrderBy.NewestFirst, limit: 50 }); console.log(historicalMessages.items); if (historicalMessages.hasNext()) { const next = await historicalMessages.next(); console.log(next); } else { console.log('End of messages'); } ``` ### React ``` import { useMessages } from '@ably/chat/react'; const MyComponent = () => { const { history } = useMessages(); const handleGetMessages = () => { // fetch the last 3 messages, oldest to newest history({ limit: 3, orderBy: OrderBy.OldestFirst }) .then((result) => console.log('Previous messages: ', result.items)); }; return (
); }; ``` ### Swift ``` let paginatedResult = try await room.messages.history(withParams: .init(orderBy: .newestFirst)) print(paginatedResult.items) if let next = try await paginatedResult.next { print(next.items) } else { print("End of messages") } ``` ### Kotlin ``` var historicalMessages = room.messages.history(orderBy = OrderBy.NewestFirst) println(historicalMessages.items.toString()) // historical messages are paginated, so we can iterate through while (historicalMessages.hasNext()) { historicalMessages = historicalMessages.next() println(historicalMessages.items.toString()) } println("End of messages") ``` ### Android ``` var historicalMessages = room.messages.history(orderBy = OrderBy.NewestFirst) println(historicalMessages.items.toString()) // historical messages are paginated, so we can iterate through while (historicalMessages.hasNext()) { historicalMessages = historicalMessages.next() println(historicalMessages.items.toString()) } println("End of messages") ```
The following optional parameters can be passed when retrieving previously sent messages: | Parameter | Description | Default | | --------- | ----------- | ------- | | start | Earliest time to retrieve messages from, as a unix timestamp in milliseconds. Messages with a timestamp equal to, or greater than, this value will be returned. | Earliest available message | | end | Latest time to retrieve messages from, as a unix timestamp in milliseconds. Messages with a timestamp less than this value will be returned. | Current time | | orderBy | The order in which to retrieve messages from; either `oldestFirst` or `newestFirst`. | `newestFirst` | | limit | Maximum number of messages to be retrieved per page, up to 1,000. | 100 | ### Understanding message ordering The `orderBy` parameter determines the messages retrieved and their return order: | Order | Retrieves | Returns | Use case | | ----- | --------- | ------- | -------- | | `newestFirst` (default) | Most recent messages | `[newest, ..., oldest]` | Standard chat application | | `oldestFirst` | Oldest messages from room start | `[oldest, ..., newest]` | Archives, exports, admin tools | For the majority of chat UIs, the default behavior of `newestFirst` will be correct. This retrieves messages from most recent to oldest, which you can then reverse to display the newest message at the bottom of the UI. ## Retrieve messages sent prior to subscribing Retrieve historical messages sent before subscribing using `historyBeforeSubscribe()`. Messages are returned in order, from the most recent to the oldest (newestFirst). This is useful for providing conversational context when a user joins or rejoins a room, ensuring continuous message history without overlap. Use the [`historyBeforeSubscribe()`](https://ably.com/docs/chat/api/javascript/messages.md?source=llms.txt#historyBeforeSubscribe)[`historyBeforeSubscribe(withParams:)`](https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messagesubscriptionresponse/historybeforesubscribe%28withparams%3A%29)[`historyBeforeSubscribe()`](https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat/com.ably.chat/-messages-subscription/history-before-subscribe.html) function returned as part of a [message subscription](https://ably.com/docs/chat/rooms/messages.md?source=llms.txt#subscribe) response to only retrieve messages that were received before the listener was subscribed to the room. This returns a paginated response, which can be queried further to retrieve the next set of messages. Use the [`historyBeforeSubscribe()`](https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat/com.ably.chat/-messages-subscription/history-before-subscribe.html) function returned as part of a [message subscription](https://ably.com/docs/chat/rooms/messages.md?source=llms.txt#subscribe) response to only retrieve messages that were received before the listener was subscribed to the room. This returns a paginated response, which can be queried further to retrieve the next set of messages. Use the [`historyBeforeSubscribe()`](https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-react.UseMessagesResponse.html#historyBeforeSubscribe) method available from the response of the `useMessages` hook to only retrieve messages that were received before the listener subscribed to the room. As long as a defined value is provided for the listener, and there are no message discontinuities, `historyBeforeSubscribe()` will return messages from the same point across component renders. If the listener becomes undefined, the subscription to messages will be removed. If you subsequently redefine the listener then `historyBeforeSubscribe()` will return messages from the new point of subscription. This returns a paginated response, which can be queried further to retrieve the next set of messages. ### Javascript ``` const { historyBeforeSubscribe } = room.messages.subscribe(() => { console.log('New message received'); }); const historicalMessages = await historyBeforeSubscribe({ limit: 50 }); console.log(historicalMessages.items); if (historicalMessages.hasNext()) { const next = await historicalMessages.next(); console.log(next); } else { console.log('End of messages'); } ``` ### React ``` import { useEffect, useState } from 'react'; import { useMessages } from '@ably/chat/react'; const MyComponent = () => { const [loading, setLoading] = useState(true); const { historyBeforeSubscribe } = useMessages({ listener: (message) => { console.log('Received message: ', message); }, onDiscontinuity: (error) => { console.log('Discontinuity detected:', error); setLoading(true); }, }); useEffect(() => { // once the listener is subscribed, `historyBeforeSubscribe` will become available if (historyBeforeSubscribe && loading) { historyBeforeSubscribe({ limit: 10 }).then((result) => { console.log('Previous messages: ', result.items); setLoading(false); }); } }, [historyBeforeSubscribe, loading]); return
...
; }; ``` ### Swift ``` let messagesSubscription = try await room.messages.subscribe() let paginatedResult = try await messagesSubscription.historyBeforeSubscribe(withParams: .init(limit: 50)) print(paginatedResult.items) if let next = try await paginatedResult.next { print(next.items) } else { print("End of messages") } ``` ### Kotlin ``` val subscription = room.messages.subscribe { println("New message received") } var historicalMessages = subscription.historyBeforeSubscribe(limit = 50) println(historicalMessages.items.toString()) while (historicalMessages.hasNext()) { historicalMessages = historicalMessages.next() println(historicalMessages.items.toString()) } println("End of messages") ``` ### Android ``` import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.* import com.ably.chat.MessagesSubscription import com.ably.chat.Room @Composable fun HistoryBeforeSubscribeComponent(room: Room) { var messages by remember { mutableStateOf>(emptyList()) } var subscription by remember { mutableStateOf(null) } DisposableEffect(room) { subscription = room.messages.subscribe { println("New message received") } onDispose { subscription?.unsubscribe() } } LaunchedEffect(subscription) { subscription?.let { sub -> var historicalMessages = sub.historyBeforeSubscribe(limit = 50) println(historicalMessages.items.toString()) messages = historicalMessages.items.map { it.text } while (historicalMessages.hasNext()) { historicalMessages = historicalMessages.next() println(historicalMessages.items.toString()) messages = messages + historicalMessages.items.map { it.text } } println("End of messages") } } // Display messages in UI Column { messages.forEach { message -> Text(message) } } } ```
The following optional parameters can be passed when retrieving messages before subscribing: | Parameter | Description | Default | | --------- | ----------- | ------- | | start | Earliest time to retrieve messages from, as a unix timestamp in milliseconds. Messages with a timestamp equal to, or greater than, this value will be returned. | Earliest available message | | end | Latest time to retrieve messages from, as a unix timestamp in milliseconds. Messages with a timestamp less than this value will be returned. | Current time | | limit | Maximum number of messages to be retrieved per page, up to 1,000. | 100 | ## Messages pagination Alternatively, you can use the [`collectAsPagingMessagesState()`](https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-extensions-compose/com.ably.chat.extensions.compose/collect-as-paging-messages-state.html) composable function to observe messages with automatic pagination support. This function provides a complete solution for displaying chat messages in Jetpack Compose by: - Subscribing to new messages in realtime and adding them to the list - Handling message updates and deletions automatically - Listening to [message reaction](https://ably.com/docs/chat/rooms/message-reactions.md?source=llms.txt) events and updating messages with reaction summaries - Managing pagination when scrolling near the end of the list using `historyBeforeSubscribe()` - Handling [discontinuities](https://ably.com/docs/chat/rooms/messages.md?source=llms.txt#discontinuity) by clearing and reloading messages - Only fetching messages when the room is in the attached state - Cleaning up resources when the composable leaves the composition The function accepts two optional parameters: - `scrollThreshold`: The number of items remaining before reaching the last item at which fetching the next page is triggered (default: 10) - `fetchSize`: The number of messages to load per page (default: 100) The returned [`PagingMessagesState`](https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-extensions-compose/com.ably.chat.extensions.compose/-paging-messages-state/index.html) provides: - `loaded`: A list of messages in reverse chronological order (newest first) - `listState`: A `LazyListState` for controlling the scroll position and triggering automatic pagination - `loading`: Whether a loading operation is in progress - `hasMore`: Whether there are more messages to load (returns `false` before the first page is fetched) - `error`: Any error encountered during loading (of type `ErrorInfo`) - `refresh()`: A function to clear the error state and retry loading ### Android ``` import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.Button import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Text import androidx.compose.runtime.Composable import com.ably.chat.Room import com.ably.chat.annotations.ExperimentalChatApi import com.ably.chat.extensions.compose.collectAsPagingMessagesState @OptIn(ExperimentalChatApi::class) @Composable fun MessagesComponent(room: Room) { val pagingMessagesState = room.collectAsPagingMessagesState( scrollThreshold = 10, fetchSize = 50, ) // Handle error state pagingMessagesState.error?.let { error -> Text("Error: ${error.message}") Button(onClick = { pagingMessagesState.refresh() }) { Text("Retry") } } LazyColumn( reverseLayout = true, state = pagingMessagesState.listState, ) { items( items = pagingMessagesState.loaded, key = { it.serial }, ) { message -> Text("${message.clientId}: ${message.text}") } // Show loading indicator when fetching messages if (pagingMessagesState.loading) { item { CircularProgressIndicator() } } } } ``` ## Related Topics - [Messages](https://ably.com/docs/chat/rooms/messages.md?source=llms.txt): Send, update, delete, and receive messages in chat rooms. - [Presence](https://ably.com/docs/chat/rooms/presence.md?source=llms.txt): Use presence to see which users are online and their user status. - [Occupancy](https://ably.com/docs/chat/rooms/occupancy.md?source=llms.txt): Use occupancy to see how many users are in a room. - [Message reactions](https://ably.com/docs/chat/rooms/message-reactions.md?source=llms.txt): React to chat messages - [Typing indicators](https://ably.com/docs/chat/rooms/typing.md?source=llms.txt): Display typing indicators in a room so that users can see when someone else is writing a message. - [Room reactions](https://ably.com/docs/chat/rooms/reactions.md?source=llms.txt): Enable users to send reactions at the room level, based on what is happening in your application, such as a goal being scored in your livestream. - [Share media](https://ably.com/docs/chat/rooms/media.md?source=llms.txt): Share media such as images, videos, or files in a chat room. - [Message replies](https://ably.com/docs/chat/rooms/replies.md?source=llms.txt): Add reply functionality to messages in a chat room. ## Documentation Index To discover additional Ably documentation: 1. Fetch [llms.txt](https://ably.com/llms.txt?source=llms.txt) for the canonical list of available pages. 2. Identify relevant URLs from that index. 3. Fetch target pages as needed. Avoid using assumed or outdated documentation paths.