diff --git a/core/screens/chat/ChatRoomView.tsx b/core/screens/chat/ChatRoomView.tsx
index d75f75284686e465e6cb15900523782d9729b1d1..5221973c5ba30745e1a30254e1a331b6963b6973 100644
--- a/core/screens/chat/ChatRoomView.tsx
+++ b/core/screens/chat/ChatRoomView.tsx
@@ -38,10 +38,10 @@ import HoliLoader from '@holi/ui/components/molecules/HoliLoader'
 import { HoliToastType, useToast } from '@holi/ui/components/molecules/HoliToastProvider'
 import HoliKeyboardSafeAreaView from '@holi/ui/components/organisms/HoliKeyboardSafeAreaView'
 import { dimensions } from '@holi/ui/styles/globalVars'
-import { createStyleSheet } from 'holi-bricks/utils'
 import { useStyles } from 'holi-bricks/hooks'
 import { TrackingEvent } from '@holi/core/tracking'
 import useTracking from '@holi/core/tracking/hooks/useTracking'
+import { createStyleSheet } from 'holi-bricks/utils'
 
 const WAIT_FOR_CHATROOM_DELAY = 5000
 
@@ -51,6 +51,8 @@ export type ChatRoomPageParams = {
 
 const { useParam } = createParamHooks<ChatRoomPageParams>()
 
+const NUMBER_OF_TEXT_MESSAGES_NEEDED_TO_FILL_A_SCREEN_AND_CAUSE_SCROLLBARS = 40
+
 const ChatRoomView = () => {
   const { t } = useTranslation()
   const { navigateBack, replaceRoute } = useRouting()
@@ -63,7 +65,7 @@ const ChatRoomView = () => {
   const { user: loggedInUser } = useLoggedInUser()
   const chatPartner = useAtomValue(useMemo(() => chatPartnerAtomCreator(roomId, loggedInUser), [loggedInUser, roomId]))
 
-  const requestMessages = useRequestRoomMessages()
+  const requestMessages = useRequestRoomMessages(NUMBER_OF_TEXT_MESSAGES_NEEDED_TO_FILL_A_SCREEN_AND_CAUSE_SCROLLBARS)
   const setScreenOptions = useSetScreenOptions()
 
   const roomMemberIds = room?.members.map((m) => m.id) ?? []
diff --git a/packages/chat/src/components/RoomMessageList/index.tsx b/packages/chat/src/components/RoomMessageList/index.tsx
index 1936fd02aa4edc2092ccfe5aa99b07eeb274973b..082409091d4d5179e125e64b1215ed66ae7496a3 100644
--- a/packages/chat/src/components/RoomMessageList/index.tsx
+++ b/packages/chat/src/components/RoomMessageList/index.tsx
@@ -1,18 +1,18 @@
-import React from 'react'
-import { useTranslation } from 'react-i18next'
-import { View } from 'react-native'
-import Animated, { LayoutAnimationConfig, ZoomInEasyDown, LinearTransition } from 'react-native-reanimated'
 import type { ChatRoomMessage } from '@holi/chat/src/client/types'
 import RoomMessageSection from '@holi/chat/src/components/RoomMessageList/RoomMessageSection'
 import { type MessageItem, isMessageSection, isTextMessageItem } from '@holi/chat/src/components/RoomMessageList/types'
 import { getSectionDisplayDate } from '@holi/chat/src/utils/date'
 import { getLocale } from '@holi/core/i18n/helpers/dateHelper'
 import { dimensions } from '@holi/ui/styles/globalVars'
+import React from 'react'
+import { useTranslation } from 'react-i18next'
+import { View } from 'react-native'
+import Animated, { LayoutAnimationConfig, ZoomInEasyDown, LinearTransition } from 'react-native-reanimated'
 
+import { useStyles } from 'holi-bricks/hooks'
+import { createStyleSheet } from 'holi-bricks/utils'
 import RoomMessage from './RoomMessage'
 import type { RoomMessageListProps } from './types'
-import { createStyleSheet } from 'holi-bricks/utils'
-import { useStyles } from 'holi-bricks/hooks'
 
 const groupByDate = (messages: ChatRoomMessage[], locale: string): MessageItem[] => {
   const groupedMessages: Record<string, MessageItem[]> = {}
diff --git a/packages/chat/src/components/__tests__/ChatHomeContent.test.tsx b/packages/chat/src/components/__tests__/ChatHomeContent.test.tsx
index 8434b12fe5849b1309e8a08a783654daa4b5ec00..f7df3ad8902df4c86aa614190bef4bff4eef18ea 100644
--- a/packages/chat/src/components/__tests__/ChatHomeContent.test.tsx
+++ b/packages/chat/src/components/__tests__/ChatHomeContent.test.tsx
@@ -131,7 +131,7 @@ describe('ChatHomeContent Component', () => {
   })
 
   it('should render login info screen if the user is not logged-in', async () => {
-    mxClient.createMessagesRequest.mockResolvedValue({ start: 'start', end: 'end', chunk: [] })
+    mxClient.createMessagesRequest.mockResolvedValue({ start: 'start', end: undefined, chunk: [] })
 
     useLoginStateMock.mockReturnValue(loggedOut)
     render(
@@ -160,7 +160,7 @@ describe('ChatHomeContent Component', () => {
 
     mxClient.getRooms.mockReturnValue([roomMock])
     mxClient.roomState.mockResolvedValue([])
-    mxClient.createMessagesRequest.mockResolvedValue({ start: 'start', end: 'end', chunk: [] })
+    mxClient.createMessagesRequest.mockResolvedValue({ start: 'start', end: undefined, chunk: [] })
 
     render(
       <MockedProvider
@@ -204,7 +204,7 @@ describe('ChatHomeContent Component', () => {
     mxClient.getRooms.mockReturnValue([roomMock])
     mxClient.roomState.mockResolvedValue([])
     mxClient.getUserId.mockReturnValue(MEMBER1_FIXTURE.id)
-    mxClient.createMessagesRequest.mockResolvedValue({ start: 'start', end: 'end', chunk: [] })
+    mxClient.createMessagesRequest.mockResolvedValue({ start: 'start', end: undefined, chunk: [] })
 
     render(
       <MockedProvider
@@ -227,7 +227,7 @@ describe('ChatHomeContent Component', () => {
   })
 
   it('should render incoming rooms', async () => {
-    mxClient.createMessagesRequest.mockResolvedValue({ start: 'start', end: 'end', chunk: [] })
+    mxClient.createMessagesRequest.mockResolvedValue({ start: 'start', end: undefined, chunk: [] })
 
     useLoginStateMock.mockReturnValue(loggedIn(HOLI_MEMBER1_ID_FIXTURE))
 
diff --git a/packages/chat/src/store/__tests__/store.messages.test.tsx b/packages/chat/src/store/__tests__/store.messages.test.tsx
index 09761a631c62dec0343daff7206426a367f40dac..9f27c4753b56dfa598d9b8ba12f5f9ef38e145f4 100644
--- a/packages/chat/src/store/__tests__/store.messages.test.tsx
+++ b/packages/chat/src/store/__tests__/store.messages.test.tsx
@@ -1,7 +1,6 @@
 import { act, renderHook } from '@testing-library/react-hooks'
-import { waitFor } from '@testing-library/react-native'
 import { Provider, createStore, useAtom } from 'jotai'
-import { EventType, type IRoomEvent, type MatrixClient, type MatrixEvent, type Room, RoomEvent } from 'matrix-js-sdk'
+import { EventType, type MatrixClient, type MatrixEvent, type Room, RoomEvent } from 'matrix-js-sdk'
 import React from 'react'
 import type { PropsWithChildren } from 'react'
 
@@ -18,19 +17,20 @@ import {
   createChatClient,
   createMatrixClientMock,
   createMatrixEventMock,
+  fakeRoomMessageEvent,
+  fakeUnrenderedEvent,
   getListenerForMatrixEvent,
 } from '@holi/chat/src/test-utils'
 import {
-  expectedMsgForEvent,
+  fakeChatRoomMessageFrom,
   initChatRoomFixture,
-  msgEvent,
   sendMessageEvent,
 } from '@holi/chat/src/test-utils/matrix-fixtures'
 
 jest.mock('matrix-js-sdk')
 jest.mock('@holi/chat/src/client/pusher')
 
-const useStoreMessagesAtomsTest = (roomId: string) => {
+const useTestRoom = (roomId: string) => {
   const response = useRoomById(roomId)
   useAtom(_roomAtomsRecordAtomSubscriber)
   useAtom(_newMessageAtomSubscriber)
@@ -38,7 +38,7 @@ const useStoreMessagesAtomsTest = (roomId: string) => {
 }
 
 describe('Chat Store - messages', () => {
-  const wrapper = ({ children }: PropsWithChildren) => <Provider store={store}>{children}</Provider>
+  const JotaiProvider = ({ children }: PropsWithChildren) => <Provider store={store}>{children}</Provider>
   let store: ReturnType<typeof createStore>
   let mxClient: jest.MockedObjectDeep<MatrixClient>
   let chatClient: ChatClient
@@ -57,7 +57,7 @@ describe('Chat Store - messages', () => {
   it('shoud receive messages in an existing room', () => {
     const { roomMock, room, creator, member } = initChatRoomFixture(mxClient)
 
-    const { result } = renderHook(() => useStoreMessagesAtomsTest(room.id), { wrapper })
+    const { result } = renderHook(() => useTestRoom(room.id), { wrapper: JotaiProvider })
     const listener = getListenerForMatrixEvent(mxClient, RoomEvent.Timeline, 'messagesListener') as (
       event: MatrixEvent,
       room: Room | undefined
@@ -81,7 +81,7 @@ describe('Chat Store - messages', () => {
   it('A new message with the same event id should replace existing message content', () => {
     const { roomMock, room, creator, member } = initChatRoomFixture(mxClient)
 
-    const { result } = renderHook(() => useStoreMessagesAtomsTest(room.id), { wrapper })
+    const { result } = renderHook(() => useTestRoom(room.id), { wrapper: JotaiProvider })
     const listener = getListenerForMatrixEvent(mxClient, RoomEvent.Timeline, 'messagesListener') as (
       event: MatrixEvent,
       room: Room | undefined
@@ -107,12 +107,12 @@ describe('Chat Store - messages', () => {
       [message1, message2, message3],
     ])
 
-    const event2Duplicate = msgEvent(room.id, member.id, 4)
+    const event2Duplicate = fakeRoomMessageEvent(room.id, member.id, 4)
     event2Duplicate.event_id = event2.event_id
     event2Duplicate.origin_server_ts = event2.origin_server_ts
     const eventMock2Duplicate = createMatrixEventMock(EventType.RoomMessage, room.id, {}, event2Duplicate)
     act(() => listener(eventMock2Duplicate, roomMock))
-    const message2Duplicate = expectedMsgForEvent(event2Duplicate)
+    const message2Duplicate = fakeChatRoomMessageFrom(event2Duplicate)
 
     // the last message and timestamp is not updated, because an existing message with an older timestamp is replaced
     expect(result.current).toStrictEqual([
@@ -121,23 +121,56 @@ describe('Chat Store - messages', () => {
     ])
   })
 
-  it('Requesting room messages should update the store in bulk, but not the room', async () => {
-    const event1 = msgEvent(room.id, creator.id, 1)
-    const event2 = msgEvent(room.id, creator.id, 2)
-    const event3 = msgEvent(room.id, creator.id, 3)
-    const roomEvents = [event1, event2, event3] as IRoomEvent[]
-    mxClient.createMessagesRequest.mockResolvedValue({ chunk: roomEvents, start: 'start', end: 'end' })
-    const { result } = renderHook(() => useRequestRoomMessages(), { wrapper })
-    result.current(room.id, false)
+  /* react ------------------> matrix sdk ------------> jotai store */
+  /* useRequestRoomMessages    createMessagesRequest    useRoomById */
 
-    const { result: result2 } = renderHook(() => useStoreMessagesAtomsTest(room.id), { wrapper })
+  it('Requesting messages for a room should update the store', async () => {
+    const event1 = fakeRoomMessageEvent(room.id, creator.id, 1)
+    const event2 = fakeRoomMessageEvent(room.id, creator.id, 2)
+    const message1 = fakeChatRoomMessageFrom(event1)
+    const message2 = fakeChatRoomMessageFrom(event2)
+    const roomEvents = [event1, event2]
+    mxClient.createMessagesRequest.mockResolvedValue({ chunk: roomEvents, start: 'start', end: undefined })
 
-    const message1 = expectedMsgForEvent(event1)
-    const message2 = expectedMsgForEvent(event2)
-    const message3 = expectedMsgForEvent(event3)
+    const { result } = renderHook(() => useRequestRoomMessages(), { wrapper: JotaiProvider })
+    await result.current(room.id, false)
+    const { result: result2 } = renderHook(() => useTestRoom(room.id), { wrapper: JotaiProvider })
 
-    await waitFor(() => {
-      expect(result2.current).toStrictEqual([room, [message3, message2, message1]])
-    })
+    expect(result2.current).toStrictEqual([room, [message2, message1]])
+  })
+
+  it('Requesting 2 messages from a room should repeatedly fetch messages until 2 messages are found', async () => {
+    const me1 = fakeRoomMessageEvent(room.id, creator.id, 1)
+    const ue2 = fakeUnrenderedEvent(room.id, creator.id, 2)
+    const ue3 = fakeUnrenderedEvent(room.id, creator.id, 3)
+    const me4 = fakeRoomMessageEvent(room.id, creator.id, 4)
+    const me5 = fakeRoomMessageEvent(room.id, creator.id, 5)
+
+    mxClient.createMessagesRequest
+      .mockResolvedValueOnce({ chunk: [me1, ue2], start: 'start', end: 'someEnd' })
+      .mockResolvedValueOnce({ chunk: [ue3, me4], start: 'start', end: 'someOtherEnd' })
+      .mockResolvedValueOnce({ chunk: [me4, me5], start: 'start', end: undefined })
+
+    const { result } = renderHook(() => useRequestRoomMessages(2), { wrapper: JotaiProvider })
+    await result.current(room.id, false)
+    const { result: result2 } = renderHook(() => useTestRoom(room.id), { wrapper: JotaiProvider })
+
+    expect(result2.current).toStrictEqual([room, [fakeChatRoomMessageFrom(me4), fakeChatRoomMessageFrom(me1)]])
+  })
+
+  it('Requesting more messages than available, will fetch until end is reached', async () => {
+    const me1 = fakeRoomMessageEvent(room.id, creator.id, 1)
+    const ue2 = fakeUnrenderedEvent(room.id, creator.id, 2)
+    const ue3 = fakeUnrenderedEvent(room.id, creator.id, 3)
+
+    mxClient.createMessagesRequest
+      .mockResolvedValueOnce({ chunk: [me1, ue2], start: 'start', end: 'someEnd' })
+      .mockResolvedValueOnce({ chunk: [ue3], start: 'start', end: undefined })
+
+    const { result } = renderHook(() => useRequestRoomMessages(2), { wrapper: JotaiProvider })
+    await result.current(room.id, false)
+    const { result: result2 } = renderHook(() => useTestRoom(room.id), { wrapper: JotaiProvider })
+
+    expect(result2.current).toStrictEqual([room, [fakeChatRoomMessageFrom(me1)]])
   })
 })
diff --git a/packages/chat/src/store/store.ts b/packages/chat/src/store/store.ts
index 2772b2e21f7af0ae673e2788beaea9b8079c278c..a805f1dd1d5659f6b0a30ceb1d2ebd32ef5b7d66 100644
--- a/packages/chat/src/store/store.ts
+++ b/packages/chat/src/store/store.ts
@@ -1,21 +1,21 @@
 import { produce } from 'immer'
 import { type Atom, atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
-import { IRoomEvent, MatrixEvent, Room, RoomMember, RoomState } from 'matrix-js-sdk'
+import type { IRoomEvent, MatrixEvent, Room, RoomMember, RoomState } from 'matrix-js-sdk'
 import { useCallback, useEffect, useMemo, useRef } from 'react'
 
-import ChatClient from '@holi/chat/src/client/ChatClient'
+import type ChatClient from '@holi/chat/src/client/ChatClient'
 import { setChatInitialized } from '@holi/chat/src/client/chatState'
 import {
-  ChatRoom,
+  type ChatRoom,
   type ChatRoomMember,
-  ChatRoomMessage,
+  type ChatRoomMessage,
   type MatrixId,
   MembershipStatus,
   type NotificationCount,
   type RoomId,
-  RoomUpdatesContent,
+  type RoomUpdatesContent,
   RoomUpdatesType,
-  SpaceChatRoom,
+  type SpaceChatRoom,
 } from '@holi/chat/src/client/types'
 import { createChatMessage } from '@holi/chat/src/client/utils'
 import {
@@ -38,7 +38,7 @@ import type {
   RoomAtomByRoomId,
   SpaceRoomAtom,
 } from '@holi/chat/src/store/types'
-import { MessageAtomsByRoomId } from '@holi/chat/src/store/types'
+import type { MessageAtomsByRoomId } from '@holi/chat/src/store/types'
 import {
   getAtomFromRecordId,
   isPrivateChatRoom,
@@ -47,7 +47,7 @@ import {
   toMatrixIdentity,
   updateSpaceRoomsNotifications,
 } from '@holi/chat/src/store/utils'
-import { User as HoliUser } from '@holi/core/domain/shared/types'
+import type { User as HoliUser } from '@holi/core/domain/shared/types'
 import { HoliError } from '@holi/core/errors/classes/HoliError'
 import { logError } from '@holi/core/errors/helpers'
 import { getLogger } from '@holi/core/helpers/logging'
@@ -118,8 +118,8 @@ export const privateRoomAtomsAtom = atom<ChatRoomAtom[]>((get) =>
 export const roomAtomsSortedByDateAtom = atom((get) => {
   const roomAtoms = Object.values(get(_roomAtomsRecordAtom))
   return roomAtoms.sort((rA, rB) => {
-    const tsA = get(rA).timestamp ?? Infinity
-    const tsB = get(rB).timestamp ?? Infinity
+    const tsA = get(rA).timestamp ?? Number.POSITIVE_INFINITY
+    const tsB = get(rB).timestamp ?? Number.POSITIVE_INFINITY
     return tsA < tsB ? 1 : -1
   })
 })
@@ -584,11 +584,7 @@ export const updateMessages = produce((thread: ChatRoomMessage[], newMessage: Ch
   Object.assign(dupMessage, newMessage)
 })
 
-/**
- * @name useRequestRoomMessages (Hook-Helper)
- * @description
- */
-export const useRequestRoomMessages = () => {
+export const useRequestRoomMessages = (numberOfMessagesToFetch = 40) => {
   const setMessages = useSetAtom(_messageBunchAtomSetter)
   const client = chatClient
   const tokenRef = useRef<{ start?: string; end?: string }>({ start: undefined, end: undefined })
@@ -596,28 +592,42 @@ export const useRequestRoomMessages = () => {
 
   return useCallback(
     async (roomId: string, resync: boolean, limit?: number) => {
-      if (!roomId || !client) return
-      if (isLoading.current || (tokenRef.current.start && !tokenRef.current.end)) return
-
-      isLoading.current = true
-      let merge = !!tokenRef.current.end
-      if (resync) {
-        tokenRef.current = { start: undefined, end: undefined }
-        merge = false
+      if (!roomId || !client) return {}
+      // skip when triggered by multiple scroll events
+      if (isLoading.current) {
+        return
+      }
+      // skip when all messages were fetched
+      if (tokenRef.current.start && !tokenRef.current.end) {
+        return
       }
 
-      await client
-        .createMessagesRequest(roomId, tokenRef.current.end, limit)
-        .then(async (resp) => {
-          const { chunk: roomEvents, start, end } = resp
-          setMessages(roomId, roomEvents, merge)
+      let totalMessages = 0
+
+      const fetchMessages = async () => {
+        isLoading.current = true
+        let merge = !!tokenRef.current.end
+        if (resync) {
+          tokenRef.current = { start: undefined, end: undefined }
+          merge = false
+        }
+
+        await client
+          .createMessagesRequest(roomId, tokenRef.current.end, limit)
+          .then(async (resp) => {
+            const { chunk: roomMessageEvents, start, end } = resp
+            setMessages(roomId, roomMessageEvents, merge)
+            tokenRef.current = { start, end }
+            totalMessages += roomMessageEvents.length
+          })
+          .finally(() => (isLoading.current = false))
+      }
 
-          // unpdate start and end tokens only after merging messages
-          tokenRef.current = { start, end }
-        })
-        .finally(() => (isLoading.current = false))
+      do {
+        await fetchMessages()
+      } while (totalMessages < numberOfMessagesToFetch && tokenRef.current.end)
     },
-    [client, setMessages]
+    [client, setMessages, numberOfMessagesToFetch]
   )
 }
 
diff --git a/packages/chat/src/test-utils/matrix-fixtures.tsx b/packages/chat/src/test-utils/matrix-fixtures.tsx
index e8a5428ebdd225b623efccee3363a493358b6ff5..12dd4fc93462492d1de5810a62adc90eb187bfe5 100644
--- a/packages/chat/src/test-utils/matrix-fixtures.tsx
+++ b/packages/chat/src/test-utils/matrix-fixtures.tsx
@@ -98,8 +98,7 @@ export const SPACE_CHILD_EVENT_CONTENT = {
   parentSpaceRoomId: SPACE_ROOM_ID_FIXTURE,
 }
 
-// Message helpers
-export const msgEvent = (roomId: string, senderId: string, eventNr: number): IEvent => {
+export const fakeRoomMessageEvent = (roomId: string, senderId: string, eventNr: number): IEvent => {
   return {
     state_key: 'dummy',
     event_id: 'id_' + eventNr,
@@ -114,7 +113,20 @@ export const msgEvent = (roomId: string, senderId: string, eventNr: number): IEv
   }
 }
 
-export const expectedMsgForEvent = (e: IEvent): ChatRoomMessage => {
+export const fakeUnrenderedEvent = (roomId: string, senderId: string, eventNr: number): IEvent => {
+  return {
+    state_key: 'someStateKey',
+    event_id: 'id_' + eventNr,
+    type: EventType.Dummy,
+    sender: senderId,
+    origin_server_ts: eventNr,
+    unsigned: {},
+    room_id: roomId,
+    content: {},
+  }
+}
+
+export const fakeChatRoomMessageFrom = (e: IEvent): ChatRoomMessage => {
   return {
     id: e.event_id,
     timestamp: e.origin_server_ts,
@@ -134,12 +146,12 @@ export const sendMessageEvent = (
   senderId: string,
   eventNr: number
 ) => {
-  const event = msgEvent(roomMock.roomId, senderId, eventNr)
+  const event = fakeRoomMessageEvent(roomMock.roomId, senderId, eventNr)
   const eventMock = createMatrixEventMock(EventType.RoomMessage, roomMock.roomId, {}, event)
 
   act(() => listener(eventMock, roomMock))
 
-  const message = expectedMsgForEvent(event)
+  const message = fakeChatRoomMessageFrom(event)
   return { message, event, eventMock }
 }