diff --git a/package.json b/package.json
index d9b67acd65804bdd711ea1f38887276bde62e4b4..76e97babb4dd20417d69703f72ef7a132a1c568b 100644
--- a/package.json
+++ b/package.json
@@ -157,6 +157,7 @@
         "temporal-polyfill": "^0.3.0",
         "ua-parser-js": "^1.0.2",
         "uuid": "^11.0.0",
+        "virtua": "^0.41.0",
         "what-input": "^5.2.10"
     },
     "devDependencies": {
diff --git a/src/components/views/rooms/MemberList/MemberListView.tsx b/src/components/views/rooms/MemberList/MemberListView.tsx
index 64c88113fa6de2e17dd3590a1bda370088fff7f5..58d5f240b3a3af6bfe55d9565b45c6fe114c25e2 100644
--- a/src/components/views/rooms/MemberList/MemberListView.tsx
+++ b/src/components/views/rooms/MemberList/MemberListView.tsx
@@ -7,7 +7,8 @@ Please see LICENSE files in the repository root for full details.
 
 import { Form } from "@vector-im/compound-web";
 import React, { useCallback, useRef, type JSX } from "react";
-import { Virtuoso, VirtuosoHandle } from "react-virtuoso";
+import { Virtualizer, VirtualizerHandle } from "virtua";
+// import { Virtuoso, VirtuosoHandle } from "react-virtuoso";
 
 import { Flex } from "../../../utils/Flex";
 import {
@@ -29,9 +30,9 @@ interface IProps {
 const MemberListView: React.FC<IProps> = (props: IProps) => {
     const vm = useMemberListViewModel(props.roomId);
     const totalRows = vm.members.length;
-    const ref = useRef<VirtuosoHandle | null>(null);
+    const ref = useRef<VirtualizerHandle | null>(null);
+    const scrollRef = useRef<HTMLDivElement | null>(null);
     const [focusedIndex, setFocusedIndex] = React.useState(-1);
-    const listRef = useRef<HTMLButtonElement | null>(null);
 
     const getRowComponent = (item: MemberWithSeparator, focused: boolean): JSX.Element => {
         if (item === SEPARATOR) {
@@ -39,19 +40,16 @@ const MemberListView: React.FC<IProps> = (props: IProps) => {
         } else if (item.member) {
             return <RoomMemberTileView member={item.member} showPresence={vm.isPresenceEnabled} focused={focused} />;
         } else {
-            return <ThreePidInviteTileView threePidInvite={item.threePidInvite} />;
+            return <ThreePidInviteTileView threePidInvite={item.threePidInvite} focused={focused} />;
         }
     };
 
     const scrollToIndex = useCallback(
         (index: number): void => {
-            ref?.current?.scrollIntoView({
-                index: index,
-                behavior: "auto",
-                done: () => {
-                    setFocusedIndex(index);
-                },
+            ref?.current?.scrollToIndex(index, {
+                align: "nearest",
             });
+            setFocusedIndex(index);
         },
         [ref],
     );
@@ -81,18 +79,6 @@ const MemberListView: React.FC<IProps> = (props: IProps) => {
         [scrollToIndex, focusedIndex, setFocusedIndex, vm, totalRows],
     );
 
-    const scrollerRef = useCallback(
-        (element: any) => {
-            if (element) {
-                element.addEventListener("keydown", keyDownCallback);
-                listRef.current = element;
-            } else {
-                listRef?.current?.removeEventListener("keydown", keyDownCallback);
-            }
-        },
-        [keyDownCallback],
-    );
-
     const onFocus = (e: React.FocusEvent): void => {
         const nextIndex = focusedIndex == -1 ? 0 : focusedIndex;
         scrollToIndex(nextIndex);
@@ -116,20 +102,24 @@ const MemberListView: React.FC<IProps> = (props: IProps) => {
                 <Form.Root>
                     <MemberListHeaderView vm={vm} />
                 </Form.Root>
-                <Virtuoso
+                <div
+                    style={{
+                        overflowY: "auto",
+                        // opt out browser's scroll anchoring on header/footer because it will conflict to scroll anchoring of virtualizer
+                        overflowAnchor: "none",
+                    }}
                     aria-label={_t("room_list|list_title")}
                     role="grid"
-                    ref={ref}
-                    style={{ height: "100%" }}
-                    scrollerRef={scrollerRef}
-                    context={{ focusedIndex }}
-                    // Don't focus on the table as a whole go straight to the first item in the list
-                    tabIndex={undefined}
-                    data={vm.members}
+                    ref={scrollRef}
                     onFocus={onFocus}
-                    itemContent={(index, member) => getRowComponent(member, index === focusedIndex)}
-                    components={{ Footer: () => footer() }}
-                />
+                    onKeyDown={keyDownCallback}
+                    tabIndex={0}
+                >
+                    <Virtualizer ref={ref} scrollRef={scrollRef}>
+                        {vm.members.map((member, index) => getRowComponent(member, index === focusedIndex))}
+                    </Virtualizer>
+                    {footer()}
+                </div>
             </Flex>
         </BaseCard>
     );
diff --git a/yarn.lock b/yarn.lock
index ec8336e202a9516d2c1499662d15ed004241e22e..8cd8b472ef667397dee86cf619fdbff5007740fe 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3729,7 +3729,7 @@
   resolved "https://registry.yarnpkg.com/@vector-im/matrix-wysiwyg/-/matrix-wysiwyg-2.38.3.tgz#cc54d8b3e9472bcd8e622126ba364ee31952cd8a"
   integrity sha512-fqo8P55Vc/t0vxpFar9RDJN5gKEjJmzrLo+O4piDbFda6VrRoqrWAtiu0Au0g6B4hRDPKIuFupk8v9Ja7q8Hvg==
   dependencies:
-    "@vector-im/matrix-wysiwyg-wasm" "link:../../../.cache/yarn/v6/npm-@vector-im-matrix-wysiwyg-2.38.3-cc54d8b3e9472bcd8e622126ba364ee31952cd8a-integrity/node_modules/bindings/wysiwyg-wasm"
+    "@vector-im/matrix-wysiwyg-wasm" "link:../../Library/Caches/Yarn/v6/npm-@vector-im-matrix-wysiwyg-2.38.3-cc54d8b3e9472bcd8e622126ba364ee31952cd8a-integrity/node_modules/bindings/wysiwyg-wasm"
 
 "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1":
   version "1.14.1"
@@ -13020,6 +13020,11 @@ vaul@^1.0.0:
   dependencies:
     "@radix-ui/react-dialog" "^1.1.1"
 
+virtua@^0.41.0:
+  version "0.41.0"
+  resolved "https://registry.yarnpkg.com/virtua/-/virtua-0.41.0.tgz#1e3c847baceb14c57e14be36272903f80a95f006"
+  integrity sha512-P8XJhPAz3mNrxAjPc+NRvD8a8+JtInKgHXuvtucThXW93U1ztUKJ2TDCAMaCPRY0CQCR465mYcOf26yQkCHWEw==
+
 vt-pbf@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac"