diff --git a/package.json b/package.json
index fa98ebac61..62e144c330 100644
--- a/package.json
+++ b/package.json
@@ -76,7 +76,7 @@
         "@sentry/browser": "^7.0.0",
         "@testing-library/react-hooks": "^8.0.1",
         "@vector-im/compound-design-tokens": "^0.1.0",
-        "@vector-im/compound-web": "1.0.0",
+        "@vector-im/compound-web": "2.0.0",
         "@zxcvbn-ts/core": "^3.0.4",
         "@zxcvbn-ts/language-common": "^3.0.4",
         "@zxcvbn-ts/language-en": "^3.0.2",
diff --git a/playwright/e2e/crypto/crypto.spec.ts b/playwright/e2e/crypto/crypto.spec.ts
index c9cad8af77..28a07a37f6 100644
--- a/playwright/e2e/crypto/crypto.spec.ts
+++ b/playwright/e2e/crypto/crypto.spec.ts
@@ -336,7 +336,7 @@ test.describe("Cryptography", function () {
             await expect(last).toContainText("Unable to decrypt message");
             const lastE2eIcon = last.locator(".mx_EventTile_e2eIcon");
             await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_decryption_failure/);
-            await lastE2eIcon.hover();
+            await lastE2eIcon.focus();
             await expect(page.getByRole("tooltip")).toContainText("This message could not be decrypted");
 
             /* Should show a red padlock for an unencrypted message in an e2e room */
@@ -356,7 +356,7 @@ test.describe("Cryptography", function () {
 
             await expect(last).toContainText("test unencrypted");
             await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
-            await lastE2eIcon.hover();
+            await lastE2eIcon.focus();
             await expect(page.getByRole("tooltip")).toContainText("Not encrypted");
 
             /* Should show no padlock for an unverified user */
@@ -389,7 +389,7 @@ test.describe("Cryptography", function () {
             await bobSecondDevice.sendMessage(testRoomId, "test encrypted from unverified");
             await expect(lastTile).toContainText("test encrypted from unverified");
             await expect(lastTileE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
-            await lastTileE2eIcon.hover();
+            await lastTileE2eIcon.focus();
             await expect(page.getByRole("tooltip")).toContainText("Encrypted by a device not verified by its owner.");
 
             /* Should show a grey padlock for a message from an unknown device */
@@ -428,7 +428,7 @@ test.describe("Cryptography", function () {
             } else {
                 await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_normal/);
             }
-            await lastE2eIcon.hover();
+            await lastE2eIcon.focus();
             await expect(page.getByRole("tooltip")).toContainText("Encrypted by an unknown or deleted device.");
         });
 
diff --git a/src/Modal.tsx b/src/Modal.tsx
index 801d617d31..f2835799fd 100644
--- a/src/Modal.tsx
+++ b/src/Modal.tsx
@@ -20,6 +20,7 @@ import ReactDOM from "react-dom";
 import classNames from "classnames";
 import { defer, sleep } from "matrix-js-sdk/src/utils";
 import { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import dis from "./dispatcher/dispatcher";
 import AsyncWrapper from "./AsyncWrapper";
@@ -373,14 +374,16 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
             const classes = classNames("mx_Dialog_wrapper mx_Dialog_staticWrapper", this.staticModal.className);
 
             const staticDialog = (
-                <div className={classes}>
-                    <div className="mx_Dialog">{this.staticModal.elem}</div>
-                    <div
-                        data-testid="dialog-background"
-                        className="mx_Dialog_background mx_Dialog_staticBackground"
-                        onClick={this.onBackgroundClick}
-                    />
-                </div>
+                <TooltipProvider>
+                    <div className={classes}>
+                        <div className="mx_Dialog">{this.staticModal.elem}</div>
+                        <div
+                            data-testid="dialog-background"
+                            className="mx_Dialog_background mx_Dialog_staticBackground"
+                            onClick={this.onBackgroundClick}
+                        />
+                    </div>
+                </TooltipProvider>
             );
 
             ReactDOM.render(staticDialog, ModalManager.getOrCreateStaticContainer());
@@ -396,14 +399,16 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
             });
 
             const dialog = (
-                <div className={classes}>
-                    <div className="mx_Dialog">{modal.elem}</div>
-                    <div
-                        data-testid="dialog-background"
-                        className="mx_Dialog_background"
-                        onClick={this.onBackgroundClick}
-                    />
-                </div>
+                <TooltipProvider>
+                    <div className={classes}>
+                        <div className="mx_Dialog">{modal.elem}</div>
+                        <div
+                            data-testid="dialog-background"
+                            className="mx_Dialog_background"
+                            onClick={this.onBackgroundClick}
+                        />
+                    </div>
+                </TooltipProvider>
             );
 
             setImmediate(() => ReactDOM.render(dialog, ModalManager.getOrCreateContainer()));
diff --git a/src/components/structures/ContextMenu.tsx b/src/components/structures/ContextMenu.tsx
index 8691c6c25d..26b52a6e72 100644
--- a/src/components/structures/ContextMenu.tsx
+++ b/src/components/structures/ContextMenu.tsx
@@ -20,6 +20,7 @@ import React, { CSSProperties, RefObject, SyntheticEvent, useRef, useState } fro
 import ReactDOM from "react-dom";
 import classNames from "classnames";
 import FocusLock from "react-focus-lock";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { Writeable } from "../../@types/common";
 import UIStore from "../../stores/UIStore";
@@ -266,6 +267,7 @@ export default class ContextMenu extends React.PureComponent<React.PropsWithChil
             wrapperClassName,
             chevronFace: propsChevronFace,
             chevronOffset: propsChevronOffset,
+            mountAsChild,
             ...props
         } = this.props;
 
@@ -628,15 +630,17 @@ export function createMenu(
     };
 
     const menu = (
-        <ContextMenu
-            {...props}
-            mountAsChild={true}
-            hasBackground={false}
-            onFinished={onFinished} // eslint-disable-line react/jsx-no-bind
-            windowResize={onFinished} // eslint-disable-line react/jsx-no-bind
-        >
-            <ElementClass {...props} onFinished={onFinished} />
-        </ContextMenu>
+        <TooltipProvider>
+            <ContextMenu
+                {...props}
+                mountAsChild={true}
+                hasBackground={false}
+                onFinished={onFinished} // eslint-disable-line react/jsx-no-bind
+                windowResize={onFinished} // eslint-disable-line react/jsx-no-bind
+            >
+                <ElementClass {...props} onFinished={onFinished} />
+            </ContextMenu>
+        </TooltipProvider>
     );
 
     ReactDOM.render(menu, getOrCreateContainer());
diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx
index b7572089c9..c998989599 100644
--- a/src/components/structures/MatrixChat.tsx
+++ b/src/components/structures/MatrixChat.tsx
@@ -34,6 +34,7 @@ import { throttle } from "lodash";
 import { CryptoEvent } from "matrix-js-sdk/src/crypto";
 import { DecryptionError } from "matrix-js-sdk/src/crypto/algorithms";
 import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 // what-input helps improve keyboard accessibility
 import "what-input";
@@ -2189,7 +2190,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 
         return (
             <ErrorBoundary>
-                <SDKContext.Provider value={this.stores}>{view}</SDKContext.Provider>
+                <SDKContext.Provider value={this.stores}>
+                    <TooltipProvider>{view}</TooltipProvider>
+                </SDKContext.Provider>
             </ErrorBoundary>
         );
     }
diff --git a/src/components/views/elements/FacePile.tsx b/src/components/views/elements/FacePile.tsx
index 0165543faa..07bbbe533e 100644
--- a/src/components/views/elements/FacePile.tsx
+++ b/src/components/views/elements/FacePile.tsx
@@ -46,7 +46,7 @@ const FacePile: FC<IProps> = ({
         tooltipLabel
             ? (m) => <MemberAvatar key={m.userId} member={m} size={size} hideTitle />
             : (m) => (
-                  <Tooltip key={m.userId} label={m.name} shortcut={tooltipShortcut}>
+                  <Tooltip key={m.userId} label={m.name} caption={tooltipShortcut}>
                       <MemberAvatar
                           member={m}
                           size={size}
@@ -72,7 +72,7 @@ const FacePile: FC<IProps> = ({
     );
 
     return tooltipLabel ? (
-        <Tooltip label={tooltipLabel} shortcut={tooltipShortcut}>
+        <Tooltip label={tooltipLabel} caption={tooltipShortcut}>
             {content}
         </Tooltip>
     ) : (
diff --git a/src/components/views/elements/PersistedElement.tsx b/src/components/views/elements/PersistedElement.tsx
index 99730ec344..37e5fc26ad 100644
--- a/src/components/views/elements/PersistedElement.tsx
+++ b/src/components/views/elements/PersistedElement.tsx
@@ -17,6 +17,7 @@ limitations under the License.
 import React, { MutableRefObject, ReactNode } from "react";
 import ReactDOM from "react-dom";
 import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import dis from "../../../dispatcher/dispatcher";
 import MatrixClientContext from "../../../contexts/MatrixClientContext";
@@ -176,9 +177,11 @@ export default class PersistedElement extends React.Component<IProps> {
     private renderApp(): void {
         const content = (
             <MatrixClientContext.Provider value={MatrixClientPeg.safeGet()}>
-                <div ref={this.collectChild} style={this.props.style}>
-                    {this.props.children}
-                </div>
+                <TooltipProvider>
+                    <div ref={this.collectChild} style={this.props.style}>
+                        {this.props.children}
+                    </div>
+                </TooltipProvider>
             </MatrixClientContext.Provider>
         );
 
diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx
index 4ffb6ce02c..cbb23f791d 100644
--- a/src/components/views/messages/TextualBody.tsx
+++ b/src/components/views/messages/TextualBody.tsx
@@ -18,6 +18,7 @@ import React, { createRef, SyntheticEvent, MouseEvent } from "react";
 import ReactDOM from "react-dom";
 import highlight from "highlight.js";
 import { MsgType } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import * as HtmlUtils from "../../../HtmlUtils";
 import { formatDate } from "../../../DateUtils";
@@ -347,7 +348,11 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
 
                 const reason = node.getAttribute("data-mx-spoiler") ?? undefined;
                 node.removeAttribute("data-mx-spoiler"); // we don't want to recurse
-                const spoiler = <Spoiler reason={reason} contentHtml={node.outerHTML} />;
+                const spoiler = (
+                    <TooltipProvider>
+                        <Spoiler reason={reason} contentHtml={node.outerHTML} />
+                    </TooltipProvider>
+                );
 
                 ReactDOM.render(spoiler, spoilerContainer);
                 node.parentNode?.replaceChild(spoilerContainer, node);
diff --git a/src/utils/exportUtils/HtmlExport.tsx b/src/utils/exportUtils/HtmlExport.tsx
index 3edf0f3cc0..6f17942007 100644
--- a/src/utils/exportUtils/HtmlExport.tsx
+++ b/src/utils/exportUtils/HtmlExport.tsx
@@ -20,6 +20,7 @@ import { Room, MatrixEvent, EventType, MsgType } from "matrix-js-sdk/src/matrix"
 import { renderToStaticMarkup } from "react-dom/server";
 import { logger } from "matrix-js-sdk/src/logger";
 import escapeHtml from "escape-html";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import Exporter from "./Exporter";
 import { mediaFromMxc } from "../../customisations/Media";
@@ -283,25 +284,27 @@ export default class HTMLExporter extends Exporter {
         return (
             <div className="mx_Export_EventWrapper" id={mxEv.getId()}>
                 <MatrixClientContext.Provider value={this.room.client}>
-                    <EventTile
-                        mxEvent={mxEv}
-                        continuation={continuation}
-                        isRedacted={mxEv.isRedacted()}
-                        replacingEventId={mxEv.replacingEventId()}
-                        forExport={true}
-                        alwaysShowTimestamps={true}
-                        showUrlPreview={false}
-                        checkUnmounting={() => false}
-                        isTwelveHour={false}
-                        last={false}
-                        lastInSection={false}
-                        permalinkCreator={this.permalinkCreator}
-                        lastSuccessful={false}
-                        isSelectedEvent={false}
-                        showReactions={false}
-                        layout={Layout.Group}
-                        showReadReceipts={false}
-                    />
+                    <TooltipProvider>
+                        <EventTile
+                            mxEvent={mxEv}
+                            continuation={continuation}
+                            isRedacted={mxEv.isRedacted()}
+                            replacingEventId={mxEv.replacingEventId()}
+                            forExport={true}
+                            alwaysShowTimestamps={true}
+                            showUrlPreview={false}
+                            checkUnmounting={() => false}
+                            isTwelveHour={false}
+                            last={false}
+                            lastInSection={false}
+                            permalinkCreator={this.permalinkCreator}
+                            lastSuccessful={false}
+                            isSelectedEvent={false}
+                            showReactions={false}
+                            layout={Layout.Group}
+                            showReadReceipts={false}
+                        />
+                    </TooltipProvider>
                 </MatrixClientContext.Provider>
             </div>
         );
diff --git a/src/utils/pillify.tsx b/src/utils/pillify.tsx
index aba0281413..f46892949e 100644
--- a/src/utils/pillify.tsx
+++ b/src/utils/pillify.tsx
@@ -18,6 +18,7 @@ import React from "react";
 import ReactDOM from "react-dom";
 import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
 import { MatrixEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import SettingsStore from "../settings/SettingsStore";
 import { Pill, PillType, pillRoomNotifLen, pillRoomNotifPos } from "../components/views/elements/Pill";
@@ -83,7 +84,9 @@ export function pillifyLinks(
                 const pillContainer = document.createElement("span");
 
                 const pill = (
-                    <Pill url={href} inMessage={true} room={room} shouldShowPillAvatar={shouldShowPillAvatar} />
+                    <TooltipProvider>
+                        <Pill url={href} inMessage={true} room={room} shouldShowPillAvatar={shouldShowPillAvatar} />
+                    </TooltipProvider>
                 );
 
                 ReactDOM.render(pill, pillContainer);
@@ -136,12 +139,14 @@ export function pillifyLinks(
 
                         const pillContainer = document.createElement("span");
                         const pill = (
-                            <Pill
-                                type={PillType.AtRoomMention}
-                                inMessage={true}
-                                room={room}
-                                shouldShowPillAvatar={shouldShowPillAvatar}
-                            />
+                            <TooltipProvider>
+                                <Pill
+                                    type={PillType.AtRoomMention}
+                                    inMessage={true}
+                                    room={room}
+                                    shouldShowPillAvatar={shouldShowPillAvatar}
+                                />
+                            </TooltipProvider>
                         );
 
                         ReactDOM.render(pill, pillContainer);
diff --git a/src/utils/tooltipify.tsx b/src/utils/tooltipify.tsx
index e3280f7fe2..8f384e59e4 100644
--- a/src/utils/tooltipify.tsx
+++ b/src/utils/tooltipify.tsx
@@ -16,6 +16,7 @@ limitations under the License.
 
 import React from "react";
 import ReactDOM from "react-dom";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import PlatformPeg from "../PlatformPeg";
 import LinkWithTooltip from "../components/views/elements/LinkWithTooltip";
@@ -60,9 +61,11 @@ export function tooltipifyLinks(rootNodes: ArrayLike<Element>, ignoredNodes: Ele
             // wrapping the link with the LinkWithTooltip component, keeping the same children. Ideally we'd do this
             // without the superfluous span but this is not something React trivially supports at this time.
             const tooltip = (
-                <LinkWithTooltip tooltip={href}>
-                    <span dangerouslySetInnerHTML={{ __html: node.innerHTML }} />
-                </LinkWithTooltip>
+                <TooltipProvider>
+                    <LinkWithTooltip tooltip={href}>
+                        <span dangerouslySetInnerHTML={{ __html: node.innerHTML }} />
+                    </LinkWithTooltip>
+                </TooltipProvider>
             );
 
             ReactDOM.render(tooltip, node);
diff --git a/test/components/structures/MessagePanel-test.tsx b/test/components/structures/MessagePanel-test.tsx
index afbe87a0a6..45fe3b4abe 100644
--- a/test/components/structures/MessagePanel-test.tsx
+++ b/test/components/structures/MessagePanel-test.tsx
@@ -19,6 +19,7 @@ import React from "react";
 import { EventEmitter } from "events";
 import { MatrixEvent, Room, RoomMember, Thread, ReceiptType } from "matrix-js-sdk/src/matrix";
 import { render } from "@testing-library/react";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import MessagePanel, { shouldFormContinuation } from "../../../src/components/structures/MessagePanel";
 import SettingsStore from "../../../src/settings/SettingsStore";
@@ -97,9 +98,10 @@ describe("MessagePanel", function () {
     const getComponent = (props = {}, roomContext: Partial<IRoomState> = {}) => (
         <MatrixClientContext.Provider value={client}>
             <RoomContext.Provider value={{ ...defaultRoomContext, ...roomContext }}>
-                <MessagePanel {...defaultProps} {...props} />
+                <TooltipProvider>
+                    <MessagePanel {...defaultProps} {...props} />
+                </TooltipProvider>
             </RoomContext.Provider>
-            );
         </MatrixClientContext.Provider>
     );
 
diff --git a/test/components/structures/RightPanel-test.tsx b/test/components/structures/RightPanel-test.tsx
index d7f5fbef11..49eea96410 100644
--- a/test/components/structures/RightPanel-test.tsx
+++ b/test/components/structures/RightPanel-test.tsx
@@ -19,6 +19,7 @@ import { render, screen, waitFor } from "@testing-library/react";
 import { jest } from "@jest/globals";
 import { mocked, MockedObject } from "jest-mock";
 import { MatrixClient } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import _RightPanel from "../../../src/components/structures/RightPanel";
 import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
@@ -122,6 +123,7 @@ describe("RightPanel", () => {
                 resizeNotifier={resizeNotifier}
                 permalinkCreator={new RoomPermalinkCreator(r1, r1.roomId)}
             />,
+            { wrapper: TooltipProvider },
         );
         // Wait for RPS room 1 updates to fire
         const rpsUpdated = waitForRpsUpdate();
diff --git a/test/components/structures/RoomView-test.tsx b/test/components/structures/RoomView-test.tsx
index 6d4dadbc8f..066f8b38a2 100644
--- a/test/components/structures/RoomView-test.tsx
+++ b/test/components/structures/RoomView-test.tsx
@@ -32,6 +32,7 @@ import {
 import { MEGOLM_ALGORITHM } from "matrix-js-sdk/src/crypto/olmlib";
 import { fireEvent, render, screen, RenderResult, waitForElementToBeRemoved, waitFor } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import {
     stubClient,
@@ -143,6 +144,7 @@ describe("RoomView", () => {
                     wrappedRef={ref as any}
                 />
             </SDKContext.Provider>,
+            { wrapper: TooltipProvider },
         );
         await flushPromises();
         return roomView;
@@ -180,6 +182,7 @@ describe("RoomView", () => {
                     onRegistered={jest.fn()}
                 />
             </SDKContext.Provider>,
+            { wrapper: TooltipProvider },
         );
         await flushPromises();
         return roomView;
diff --git a/test/components/structures/SpaceHierarchy-test.tsx b/test/components/structures/SpaceHierarchy-test.tsx
index 0fb8dd52a1..3b851c5a61 100644
--- a/test/components/structures/SpaceHierarchy-test.tsx
+++ b/test/components/structures/SpaceHierarchy-test.tsx
@@ -19,6 +19,7 @@ import { mocked } from "jest-mock";
 import { fireEvent, render, screen, waitFor, waitForElementToBeRemoved } from "@testing-library/react";
 import { MatrixClient, Room, HierarchyRoom } from "matrix-js-sdk/src/matrix";
 import { RoomHierarchy } from "matrix-js-sdk/src/room-hierarchy";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
 import { mkStubRoom, stubClient } from "../../test-utils";
@@ -262,7 +263,9 @@ describe("SpaceHierarchy", () => {
         };
         const getComponent = (props = {}): React.ReactElement => (
             <MatrixClientContext.Provider value={client}>
-                <SpaceHierarchy {...defaultProps} {...props} />;
+                <TooltipProvider>
+                    <SpaceHierarchy {...defaultProps} {...props} />
+                </TooltipProvider>
             </MatrixClientContext.Provider>
         );
 
diff --git a/test/components/structures/ThreadPanel-test.tsx b/test/components/structures/ThreadPanel-test.tsx
index b3025bb3ad..9c8bed0af6 100644
--- a/test/components/structures/ThreadPanel-test.tsx
+++ b/test/components/structures/ThreadPanel-test.tsx
@@ -25,6 +25,7 @@ import {
     FeatureSupport,
     Thread,
 } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import ThreadPanel, { ThreadFilterType, ThreadPanelHeader } from "../../../src/components/structures/ThreadPanel";
 import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
@@ -58,6 +59,7 @@ describe("ThreadPanel", () => {
                     filterOption={ThreadFilterType.My}
                     setFilterOption={() => undefined}
                 />,
+                { wrapper: TooltipProvider },
             );
             expect(asFragment()).toMatchSnapshot();
         });
@@ -69,6 +71,7 @@ describe("ThreadPanel", () => {
                     filterOption={ThreadFilterType.All}
                     setFilterOption={() => undefined}
                 />,
+                { wrapper: TooltipProvider },
             );
             const found = container.querySelector(".mx_ThreadPanel_dropdown");
             expect(found).toBeTruthy();
@@ -84,6 +87,7 @@ describe("ThreadPanel", () => {
                     filterOption={ThreadFilterType.All}
                     setFilterOption={() => undefined}
                 />,
+                { wrapper: TooltipProvider },
             );
             fireEvent.click(container.querySelector(".mx_ThreadPanel_dropdown")!);
             const found = screen.queryAllByRole("menuitemradio");
@@ -207,7 +211,7 @@ describe("ThreadPanel", () => {
             myThreads!.addLiveEvent(ownThread.rootEvent);
 
             let events: EventData[] = [];
-            const renderResult = render(<TestThreadPanel />);
+            const renderResult = render(<TestThreadPanel />, { wrapper: TooltipProvider });
             await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
             await waitFor(() => {
                 events = findEvents(renderResult.container);
@@ -253,7 +257,7 @@ describe("ThreadPanel", () => {
             allThreads!.addLiveEvent(otherThread.rootEvent);
 
             let events: EventData[] = [];
-            const renderResult = render(<TestThreadPanel />);
+            const renderResult = render(<TestThreadPanel />, { wrapper: TooltipProvider });
             await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
             await waitFor(() => {
                 events = findEvents(renderResult.container);
diff --git a/test/components/structures/TimelinePanel-test.tsx b/test/components/structures/TimelinePanel-test.tsx
index 82fd7cac31..c68e37c83f 100644
--- a/test/components/structures/TimelinePanel-test.tsx
+++ b/test/components/structures/TimelinePanel-test.tsx
@@ -38,6 +38,7 @@ import {
 import React, { createRef } from "react";
 import { Mocked, mocked } from "jest-mock";
 import { forEachRight } from "lodash";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import TimelinePanel from "../../../src/components/structures/TimelinePanel";
 import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
@@ -210,6 +211,7 @@ describe("TimelinePanel", () => {
                     manageReadReceipts={true}
                     ref={ref}
                 />,
+                { wrapper: TooltipProvider },
             );
             await flushPromises();
             timelinePanel = ref.current!;
@@ -389,7 +391,7 @@ describe("TimelinePanel", () => {
             onEventScrolledIntoView: jest.fn(),
         };
 
-        const { rerender } = render(<TimelinePanel {...props} />);
+        const { rerender } = render(<TimelinePanel {...props} />, { wrapper: TooltipProvider });
         expect(props.onEventScrolledIntoView).toHaveBeenCalledWith(undefined);
         props.eventId = events[1].getId();
         rerender(<TimelinePanel {...props} />);
@@ -406,7 +408,9 @@ describe("TimelinePanel", () => {
         setupPagination(client, timeline, eventsPage1, null);
 
         await withScrollPanelMountSpy(async (mountSpy) => {
-            const { container } = render(<TimelinePanel {...getProps(room, events)} timelineSet={timelineSet} />);
+            const { container } = render(<TimelinePanel {...getProps(room, events)} timelineSet={timelineSet} />, {
+                wrapper: TooltipProvider,
+            });
 
             await waitFor(() => expectEvents(container, [events[1]]));
 
@@ -423,7 +427,7 @@ describe("TimelinePanel", () => {
         const [, room, events] = setupTestData();
 
         await withScrollPanelMountSpy(async (mountSpy) => {
-            const { container } = render(<TimelinePanel {...getProps(room, events)} />);
+            const { container } = render(<TimelinePanel {...getProps(room, events)} />, { wrapper: TooltipProvider });
 
             await waitFor(() => expectEvents(container, [events[0], events[1]]));
 
@@ -450,7 +454,7 @@ describe("TimelinePanel", () => {
 
             const paginateSpy = jest.spyOn(TimelineWindow.prototype, "paginate").mockClear();
 
-            render(<TimelinePanel {...props} />);
+            render(<TimelinePanel {...props} />, { wrapper: TooltipProvider });
 
             const event = new MatrixEvent({ type: RoomEvent.Timeline, origin_server_ts: 0 });
             const data = { timeline: otherTimeline, liveEvent: true };
@@ -466,7 +470,7 @@ describe("TimelinePanel", () => {
 
             const paginateSpy = jest.spyOn(TimelineWindow.prototype, "paginate").mockClear();
 
-            render(<TimelinePanel {...props} />);
+            render(<TimelinePanel {...props} />, { wrapper: TooltipProvider });
 
             const event = new MatrixEvent({ type: RoomEvent.Timeline, origin_server_ts: 0 });
             const data = { timeline: props.timelineSet.getLiveTimeline(), liveEvent: false };
@@ -482,7 +486,7 @@ describe("TimelinePanel", () => {
 
             const paginateSpy = jest.spyOn(TimelineWindow.prototype, "paginate").mockClear();
 
-            render(<TimelinePanel {...props} />);
+            render(<TimelinePanel {...props} />, { wrapper: TooltipProvider });
 
             const event = new MatrixEvent({ type: RoomEvent.Timeline, origin_server_ts: 0 });
             const data = { timeline: props.timelineSet.getLiveTimeline(), liveEvent: false };
@@ -499,7 +503,7 @@ describe("TimelinePanel", () => {
 
             const paginateSpy = jest.spyOn(TimelineWindow.prototype, "paginate").mockClear();
 
-            render(<TimelinePanel {...props} />);
+            render(<TimelinePanel {...props} />, { wrapper: TooltipProvider });
 
             const event = new MatrixEvent({ type: RoomEvent.Timeline, origin_server_ts: 0 });
             const data = { timeline: props.timelineSet.getLiveTimeline(), liveEvent: true };
@@ -522,7 +526,7 @@ describe("TimelinePanel", () => {
 
             const paginateSpy = jest.spyOn(TimelineWindow.prototype, "paginate").mockClear();
 
-            render(<TimelinePanel {...props} />);
+            render(<TimelinePanel {...props} />, { wrapper: TooltipProvider });
 
             await flushPromises();
 
@@ -563,6 +567,7 @@ describe("TimelinePanel", () => {
                     overlayTimelineSet={overlayTimelineSet}
                     overlayTimelineSetFilter={isCallEvent}
                 />,
+                { wrapper: TooltipProvider },
             );
             await waitFor(() =>
                 expectEvents(container, [
@@ -602,6 +607,7 @@ describe("TimelinePanel", () => {
 
             const { container } = render(
                 <TimelinePanel {...getProps(room, events)} overlayTimelineSet={overlayTimelineSet} />,
+                { wrapper: TooltipProvider },
             );
 
             await waitFor(() =>
@@ -633,6 +639,7 @@ describe("TimelinePanel", () => {
 
             const { container } = render(
                 <TimelinePanel {...getProps(room, events)} overlayTimelineSet={overlayTimelineSet} />,
+                { wrapper: TooltipProvider },
             );
 
             await waitFor(() =>
@@ -664,6 +671,7 @@ describe("TimelinePanel", () => {
 
             const { container } = render(
                 <TimelinePanel {...getProps(room, events)} overlayTimelineSet={overlayTimelineSet} />,
+                { wrapper: TooltipProvider },
             );
 
             await waitFor(() =>
@@ -698,6 +706,7 @@ describe("TimelinePanel", () => {
                         timelineSet={timelineSet}
                         overlayTimelineSet={overlayTimelineSet}
                     />,
+                    { wrapper: TooltipProvider },
                 );
 
                 await waitFor(() => expectEvents(container, [overlayEvents[0], events[0]]));
@@ -771,6 +780,7 @@ describe("TimelinePanel", () => {
             await withScrollPanelMountSpy(async (mountSpy) => {
                 const { container } = render(
                     <TimelinePanel {...getProps(room, events)} overlayTimelineSet={overlayTimelineSet} />,
+                    { wrapper: TooltipProvider },
                 );
 
                 await waitFor(() =>
@@ -883,6 +893,7 @@ describe("TimelinePanel", () => {
                 <MatrixClientContext.Provider value={client}>
                     <TimelinePanel timelineSet={allThreads} manageReadReceipts sendReadReceiptOnLoad />
                 </MatrixClientContext.Provider>,
+                { wrapper: TooltipProvider },
             );
             await dom.findByText("RootEvent");
             await dom.findByText("ReplyEvent1");
@@ -936,6 +947,7 @@ describe("TimelinePanel", () => {
                 <MatrixClientContext.Provider value={client}>
                     <TimelinePanel timelineSet={allThreads} manageReadReceipts sendReadReceiptOnLoad />
                 </MatrixClientContext.Provider>,
+                { wrapper: TooltipProvider },
             );
             await dom.findByText("RootEvent");
             await dom.findByText("ReplyEvent1");
@@ -1004,6 +1016,7 @@ describe("TimelinePanel", () => {
             <MatrixClientContext.Provider value={client}>
                 <TimelinePanel timelineSet={timelineSet} manageReadReceipts={true} sendReadReceiptOnLoad={true} />
             </MatrixClientContext.Provider>,
+            { wrapper: TooltipProvider },
         );
 
         await waitFor(() => expect(screen.queryByRole("progressbar")).toBeNull());
diff --git a/test/components/structures/__snapshots__/MessagePanel-test.tsx.snap b/test/components/structures/__snapshots__/MessagePanel-test.tsx.snap
index 345e8ee507..8746cc34a4 100644
--- a/test/components/structures/__snapshots__/MessagePanel-test.tsx.snap
+++ b/test/components/structures/__snapshots__/MessagePanel-test.tsx.snap
@@ -16,7 +16,6 @@ exports[`MessagePanel should handle large numbers of hidden events quickly 1`] =
       />
     </div>
   </div>
-  );
 </DocumentFragment>
 `;
 
@@ -119,7 +118,6 @@ exports[`MessagePanel should handle lots of membership events quickly 1`] = `
       </ol>
     </div>
   </div>
-  );
 </DocumentFragment>
 `;
 
@@ -139,6 +137,5 @@ exports[`MessagePanel should handle lots of room creation events quickly 1`] = `
       />
     </div>
   </div>
-  );
 </DocumentFragment>
 `;
diff --git a/test/components/structures/__snapshots__/SpaceHierarchy-test.tsx.snap b/test/components/structures/__snapshots__/SpaceHierarchy-test.tsx.snap
index 6b70625ea0..a21da49dea 100644
--- a/test/components/structures/__snapshots__/SpaceHierarchy-test.tsx.snap
+++ b/test/components/structures/__snapshots__/SpaceHierarchy-test.tsx.snap
@@ -351,6 +351,5 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
       </div>
     </li>
   </ul>
-  ;
 </DocumentFragment>
 `;
diff --git a/test/components/structures/auth/ForgotPassword-test.tsx b/test/components/structures/auth/ForgotPassword-test.tsx
index 6977fe9ec4..0dbb495523 100644
--- a/test/components/structures/auth/ForgotPassword-test.tsx
+++ b/test/components/structures/auth/ForgotPassword-test.tsx
@@ -19,6 +19,7 @@ import { mocked } from "jest-mock";
 import { act, render, RenderResult, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
 import { MatrixClient, createClient } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import ForgotPassword from "../../../../src/components/structures/auth/ForgotPassword";
 import { ValidatedServerConfig } from "../../../../src/utils/ValidatedServerConfig";
@@ -102,6 +103,7 @@ describe("<ForgotPassword>", () => {
         beforeEach(() => {
             renderResult = render(
                 <ForgotPassword serverConfig={serverConfig} onComplete={onComplete} onLoginClick={onLoginClick} />,
+                { wrapper: TooltipProvider },
             );
         });
 
diff --git a/test/components/views/avatars/DecoratedRoomAvatar-test.tsx b/test/components/views/avatars/DecoratedRoomAvatar-test.tsx
index 537e2983c6..a43d41c086 100644
--- a/test/components/views/avatars/DecoratedRoomAvatar-test.tsx
+++ b/test/components/views/avatars/DecoratedRoomAvatar-test.tsx
@@ -19,6 +19,7 @@ import { mocked } from "jest-mock";
 import { JoinRule, MatrixClient, PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix";
 import React from "react";
 import userEvent from "@testing-library/user-event";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
 import { stubClient } from "../../../test-utils";
@@ -47,7 +48,9 @@ describe("DecoratedRoomAvatar", () => {
 
     it("shows an avatar with globe icon and tooltip for public room", async () => {
         room.getJoinRule = jest.fn().mockReturnValue(JoinRule.Public);
-        const { container, asFragment } = render(<DecoratedRoomAvatar room={room} size="32px" />);
+        const { container, asFragment } = render(<DecoratedRoomAvatar room={room} size="32px" />, {
+            wrapper: TooltipProvider,
+        });
 
         const globe = container.querySelector(".mx_DecoratedRoomAvatar_icon_globe")!;
         expect(globe).toBeVisible();
diff --git a/test/components/views/beacon/BeaconListItem-test.tsx b/test/components/views/beacon/BeaconListItem-test.tsx
index 10497b369f..6050e2b6cd 100644
--- a/test/components/views/beacon/BeaconListItem-test.tsx
+++ b/test/components/views/beacon/BeaconListItem-test.tsx
@@ -17,6 +17,7 @@ limitations under the License.
 import React from "react";
 import { act, fireEvent, render } from "@testing-library/react";
 import { Beacon, RoomMember, MatrixEvent, LocationAssetType } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import BeaconListItem from "../../../../src/components/views/beacon/BeaconListItem";
 import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
@@ -75,6 +76,7 @@ describe("<BeaconListItem />", () => {
             <MatrixClientContext.Provider value={mockClient}>
                 <BeaconListItem {...defaultProps} {...props} />
             </MatrixClientContext.Provider>,
+            { wrapper: TooltipProvider },
         );
 
     const setupRoomWithBeacons = (beaconInfoEvents: MatrixEvent[], locationEvents?: MatrixEvent[]): Beacon[] => {
diff --git a/test/components/views/beacon/BeaconViewDialog-test.tsx b/test/components/views/beacon/BeaconViewDialog-test.tsx
index cee880c966..b796253e2a 100644
--- a/test/components/views/beacon/BeaconViewDialog-test.tsx
+++ b/test/components/views/beacon/BeaconViewDialog-test.tsx
@@ -19,6 +19,7 @@ import { act, fireEvent, render, RenderResult } from "@testing-library/react";
 import { MatrixClient, MatrixEvent, Room, RoomMember, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
 import * as maplibregl from "maplibre-gl";
 import { mocked } from "jest-mock";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import BeaconViewDialog from "../../../../src/components/views/beacon/BeaconViewDialog";
 import {
@@ -79,7 +80,8 @@ describe("<BeaconViewDialog />", () => {
         matrixClient: mockClient as MatrixClient,
     };
 
-    const getComponent = (props = {}): RenderResult => render(<BeaconViewDialog {...defaultProps} {...props} />);
+    const getComponent = (props = {}): RenderResult =>
+        render(<BeaconViewDialog {...defaultProps} {...props} />, { wrapper: TooltipProvider });
 
     const openSidebar = (getByTestId: RenderResult["getByTestId"]) => {
         fireEvent.click(getByTestId("beacon-view-dialog-open-sidebar"));
diff --git a/test/components/views/beacon/DialogSidebar-test.tsx b/test/components/views/beacon/DialogSidebar-test.tsx
index a0def1f445..0c14d334df 100644
--- a/test/components/views/beacon/DialogSidebar-test.tsx
+++ b/test/components/views/beacon/DialogSidebar-test.tsx
@@ -16,6 +16,7 @@ limitations under the License.
 
 import React, { ComponentProps } from "react";
 import { act, fireEvent, render } from "@testing-library/react";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import DialogSidebar from "../../../../src/components/views/beacon/DialogSidebar";
 import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
@@ -52,8 +53,9 @@ describe("<DialogSidebar />", () => {
 
     const getComponent = (props = {}) => (
         <MatrixClientContext.Provider value={client}>
-            <DialogSidebar {...defaultProps} {...props} />
-            );
+            <TooltipProvider>
+                <DialogSidebar {...defaultProps} {...props} />
+            </TooltipProvider>
         </MatrixClientContext.Provider>
     );
 
diff --git a/test/components/views/beacon/ShareLatestLocation-test.tsx b/test/components/views/beacon/ShareLatestLocation-test.tsx
index 654b3dc73a..279e21671f 100644
--- a/test/components/views/beacon/ShareLatestLocation-test.tsx
+++ b/test/components/views/beacon/ShareLatestLocation-test.tsx
@@ -16,6 +16,7 @@ limitations under the License.
 
 import React from "react";
 import { fireEvent, render } from "@testing-library/react";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import ShareLatestLocation from "../../../../src/components/views/beacon/ShareLatestLocation";
 import { copyPlaintext } from "../../../../src/utils/strings";
@@ -32,7 +33,8 @@ describe("<ShareLatestLocation />", () => {
             timestamp: 123,
         },
     };
-    const getComponent = (props = {}) => render(<ShareLatestLocation {...defaultProps} {...props} />);
+    const getComponent = (props = {}) =>
+        render(<ShareLatestLocation {...defaultProps} {...props} />, { wrapper: TooltipProvider });
 
     beforeEach(() => {
         jest.clearAllMocks();
diff --git a/test/components/views/beacon/__snapshots__/DialogSidebar-test.tsx.snap b/test/components/views/beacon/__snapshots__/DialogSidebar-test.tsx.snap
index a91a0e6f09..0fdb0fc27c 100644
--- a/test/components/views/beacon/__snapshots__/DialogSidebar-test.tsx.snap
+++ b/test/components/views/beacon/__snapshots__/DialogSidebar-test.tsx.snap
@@ -96,7 +96,6 @@ exports[`<DialogSidebar /> renders sidebar correctly with beacons 1`] = `
       </li>
     </ol>
   </div>
-  );
 </div>
 `;
 
@@ -131,6 +130,5 @@ exports[`<DialogSidebar /> renders sidebar correctly without beacons 1`] = `
       No live locations
     </div>
   </div>
-  );
 </div>
 `;
diff --git a/test/components/views/dialogs/ServerPickerDialog-test.tsx b/test/components/views/dialogs/ServerPickerDialog-test.tsx
index 46594ea635..02930c9581 100644
--- a/test/components/views/dialogs/ServerPickerDialog-test.tsx
+++ b/test/components/views/dialogs/ServerPickerDialog-test.tsx
@@ -17,6 +17,7 @@ limitations under the License.
 import React from "react";
 import { fireEvent, render, screen } from "@testing-library/react";
 import fetchMock from "fetch-mock-jest";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import ServerPickerDialog from "../../../../src/components/views/dialogs/ServerPickerDialog";
 import SdkConfig from "../../../../src/SdkConfig";
@@ -55,7 +56,7 @@ describe("<ServerPickerDialog />", () => {
             onFinished: any;
             serverConfig: ValidatedServerConfig;
         }> = {},
-    ) => render(<ServerPickerDialog {...defaultProps} {...props} />);
+    ) => render(<ServerPickerDialog {...defaultProps} {...props} />, { wrapper: TooltipProvider });
 
     beforeEach(() => {
         SdkConfig.add({
diff --git a/test/components/views/elements/FacePile-test.tsx b/test/components/views/elements/FacePile-test.tsx
index 31f6451561..3e002145ae 100644
--- a/test/components/views/elements/FacePile-test.tsx
+++ b/test/components/views/elements/FacePile-test.tsx
@@ -14,6 +14,7 @@ limitations under the License.
 
 import { render } from "@testing-library/react";
 import React from "react";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import FacePile from "../../../../src/components/views/elements/FacePile";
 import { mkRoomMember } from "../../../test-utils";
@@ -24,6 +25,7 @@ describe("<FacePile />", () => {
 
         const { asFragment } = render(
             <FacePile members={[member]} size="36px" overflow={false} tooltipLabel="tooltip" />,
+            { wrapper: TooltipProvider },
         );
 
         expect(asFragment()).toMatchSnapshot();
diff --git a/test/components/views/elements/InfoTooltip-test.tsx b/test/components/views/elements/InfoTooltip-test.tsx
index 049fa87847..226497cea8 100644
--- a/test/components/views/elements/InfoTooltip-test.tsx
+++ b/test/components/views/elements/InfoTooltip-test.tsx
@@ -17,12 +17,15 @@ limitations under the License.
 import React from "react";
 import userEvent from "@testing-library/user-event";
 import { render, waitFor } from "@testing-library/react";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import InfoTooltip from "../../../../src/components/views/elements/InfoTooltip";
 
 describe("InfoTooltip", () => {
     it("should show tooltip on hover", async () => {
-        const { getByText, asFragment } = render(<InfoTooltip tooltip="Tooltip text">Trigger text</InfoTooltip>);
+        const { getByText, asFragment } = render(<InfoTooltip tooltip="Tooltip text">Trigger text</InfoTooltip>, {
+            wrapper: TooltipProvider,
+        });
 
         const trigger = getByText("Trigger text");
         expect(trigger).toBeVisible();
diff --git a/test/components/views/elements/Pill-test.tsx b/test/components/views/elements/Pill-test.tsx
index cdde49cc56..ea1a8b34eb 100644
--- a/test/components/views/elements/Pill-test.tsx
+++ b/test/components/views/elements/Pill-test.tsx
@@ -19,6 +19,7 @@ import { act, render, RenderResult, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
 import { mocked, Mocked } from "jest-mock";
 import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import dis from "../../../../src/dispatcher/dispatcher";
 import { Pill, PillProps, PillType } from "../../../../src/components/views/elements/Pill";
@@ -64,6 +65,7 @@ describe("<Pill>", () => {
             <div onClick={pillParentClickHandler}>
                 <Pill {...withDefault} />
             </div>,
+            { wrapper: TooltipProvider },
         );
     };
 
diff --git a/test/components/views/elements/RoomFacePile-test.tsx b/test/components/views/elements/RoomFacePile-test.tsx
index e733fdcf5f..aa75e92d21 100644
--- a/test/components/views/elements/RoomFacePile-test.tsx
+++ b/test/components/views/elements/RoomFacePile-test.tsx
@@ -14,6 +14,7 @@ limitations under the License.
 
 import { render } from "@testing-library/react";
 import React from "react";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { mkRoom, mkRoomMember, stubClient, withClientContextRenderOptions } from "../../../test-utils";
 import RoomFacePile from "../../../../src/components/views/elements/RoomFacePile";
@@ -29,7 +30,9 @@ describe("<RoomFacePile />", () => {
         jest.spyOn(room, "getJoinedMembers").mockReturnValue([mkRoomMember(room.roomId, "@bob:example.org", "join")]);
 
         const { asFragment } = render(
-            <RoomFacePile onlyKnownUsers={false} room={room} />,
+            <TooltipProvider>
+                <RoomFacePile onlyKnownUsers={false} room={room} />
+            </TooltipProvider>,
             withClientContextRenderOptions(MatrixClientPeg.get()!),
         );
 
diff --git a/test/components/views/messages/CallEvent-test.tsx b/test/components/views/messages/CallEvent-test.tsx
index 2b29f866c6..3e1167beb9 100644
--- a/test/components/views/messages/CallEvent-test.tsx
+++ b/test/components/views/messages/CallEvent-test.tsx
@@ -19,6 +19,7 @@ import { render, screen, act, cleanup, fireEvent, waitFor } from "@testing-libra
 import { mocked, Mocked } from "jest-mock";
 import { Room, RoomStateEvent, MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/matrix";
 import { ClientWidgetApi, Widget } from "matrix-widget-api";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import type { RoomMember } from "matrix-js-sdk/src/matrix";
 import {
@@ -102,7 +103,7 @@ describe("CallEvent", () => {
     });
 
     const renderEvent = () => {
-        render(<CallEvent mxEvent={call.event} />);
+        render(<CallEvent mxEvent={call.event} />, { wrapper: TooltipProvider });
     };
 
     it("shows a message and duration if the call was ended", () => {
diff --git a/test/components/views/messages/MStickerBody-test.tsx b/test/components/views/messages/MStickerBody-test.tsx
index e541149a79..ee0fb5c337 100644
--- a/test/components/views/messages/MStickerBody-test.tsx
+++ b/test/components/views/messages/MStickerBody-test.tsx
@@ -19,6 +19,7 @@ import { render, screen } from "@testing-library/react";
 import { EventType, getHttpUriForMxc, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
 import fetchMock from "fetch-mock-jest";
 import userEvent from "@testing-library/user-event";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
 import {
@@ -85,7 +86,7 @@ describe("<MStickerBody/>", () => {
     it("should show a tooltip on hover", async () => {
         fetchMock.getOnce(url, { status: 200 });
 
-        render(<MStickerBody {...props} mxEvent={mediaEvent} />);
+        render(<MStickerBody {...props} mxEvent={mediaEvent} />, { wrapper: TooltipProvider });
 
         expect(screen.queryByRole("tooltip")).toBeNull();
         await userEvent.hover(screen.getByRole("img"));
diff --git a/test/components/views/messages/MessageTimestamp-test.tsx b/test/components/views/messages/MessageTimestamp-test.tsx
index 05a1ca12c1..5b8df86d6c 100644
--- a/test/components/views/messages/MessageTimestamp-test.tsx
+++ b/test/components/views/messages/MessageTimestamp-test.tsx
@@ -17,6 +17,7 @@ limitations under the License.
 import React from "react";
 import { render, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import MessageTimestamp from "../../../../src/components/views/messages/MessageTimestamp";
 
@@ -30,7 +31,7 @@ describe("MessageTimestamp", () => {
     const DAY_MS = HOUR_MS * 24;
 
     it("should render HH:MM", () => {
-        const { asFragment } = render(<MessageTimestamp ts={nowDate.getTime()} />);
+        const { asFragment } = render(<MessageTimestamp ts={nowDate.getTime()} />, { wrapper: TooltipProvider });
         expect(asFragment()).toMatchInlineSnapshot(`
             <DocumentFragment>
               <span
@@ -46,7 +47,7 @@ describe("MessageTimestamp", () => {
     });
 
     it("should show full date & time on hover", async () => {
-        const { container } = render(<MessageTimestamp ts={nowDate.getTime()} />);
+        const { container } = render(<MessageTimestamp ts={nowDate.getTime()} />, { wrapper: TooltipProvider });
         await userEvent.hover(container.querySelector(".mx_MessageTimestamp")!);
         expect((await screen.findByRole("tooltip")).textContent).toMatchInlineSnapshot(`"Fri, Dec 17, 2021, 08:09:00"`);
     });
@@ -54,6 +55,7 @@ describe("MessageTimestamp", () => {
     it("should show sent & received time on hover if passed", async () => {
         const { container } = render(
             <MessageTimestamp ts={nowDate.getTime()} receivedTs={nowDate.getTime() + DAY_MS} />,
+            { wrapper: TooltipProvider },
         );
         await userEvent.hover(container.querySelector(".mx_MessageTimestamp")!);
         expect((await screen.findByRole("tooltip")).textContent).toMatchInlineSnapshot(
diff --git a/test/components/views/polls/pollHistory/PollHistory-test.tsx b/test/components/views/polls/pollHistory/PollHistory-test.tsx
index 2a5fc80168..054961cb2e 100644
--- a/test/components/views/polls/pollHistory/PollHistory-test.tsx
+++ b/test/components/views/polls/pollHistory/PollHistory-test.tsx
@@ -17,6 +17,7 @@ limitations under the License.
 import React from "react";
 import { act, fireEvent, render } from "@testing-library/react";
 import { Filter, EventTimeline, Room, MatrixEvent, M_POLL_START } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { PollHistory } from "../../../../../src/components/views/polls/pollHistory/PollHistory";
 import {
@@ -67,7 +68,9 @@ describe("<PollHistory />", () => {
     const getComponent = () =>
         render(<PollHistory {...defaultProps} />, {
             wrapper: ({ children }) => (
-                <MatrixClientContext.Provider value={mockClient}>{children}</MatrixClientContext.Provider>
+                <MatrixClientContext.Provider value={mockClient}>
+                    <TooltipProvider>{children}</TooltipProvider>
+                </MatrixClientContext.Provider>
             ),
         });
 
diff --git a/test/components/views/polls/pollHistory/PollListItem-test.tsx b/test/components/views/polls/pollHistory/PollListItem-test.tsx
index b48cddb8d8..1b49ca922b 100644
--- a/test/components/views/polls/pollHistory/PollListItem-test.tsx
+++ b/test/components/views/polls/pollHistory/PollListItem-test.tsx
@@ -17,6 +17,7 @@ limitations under the License.
 import React from "react";
 import { fireEvent, render } from "@testing-library/react";
 import { MatrixEvent } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { PollListItem } from "../../../../../src/components/views/polls/pollHistory/PollListItem";
 import { makePollStartEvent, mockIntlDateTimeFormat, unmockIntlDateTimeFormat } from "../../../../test-utils";
@@ -25,7 +26,8 @@ describe("<PollListItem />", () => {
     const event = makePollStartEvent("Question?", "@me:domain.org");
     event.getContent().origin;
     const defaultProps = { event, onClick: jest.fn() };
-    const getComponent = (props = {}) => render(<PollListItem {...defaultProps} {...props} />);
+    const getComponent = (props = {}) =>
+        render(<PollListItem {...defaultProps} {...props} />, { wrapper: TooltipProvider });
 
     beforeAll(() => {
         // mock default locale to en-GB and set timezone
diff --git a/test/components/views/polls/pollHistory/PollListItemEnded-test.tsx b/test/components/views/polls/pollHistory/PollListItemEnded-test.tsx
index 7bf27ee447..371547e67d 100644
--- a/test/components/views/polls/pollHistory/PollListItemEnded-test.tsx
+++ b/test/components/views/polls/pollHistory/PollListItemEnded-test.tsx
@@ -17,6 +17,7 @@ limitations under the License.
 import React from "react";
 import { render } from "@testing-library/react";
 import { MatrixEvent, Poll, Room, M_TEXT } from "matrix-js-sdk/src/matrix";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { PollListItemEnded } from "../../../../../src/components/views/polls/pollHistory/PollListItemEnded";
 import {
@@ -60,7 +61,7 @@ describe("<PollListItemEnded />", () => {
     const pollEndEvent = makePollEndEvent(pollId, roomId, userId, timestamp + 60000);
 
     const getComponent = (props: { event: MatrixEvent; poll: Poll }) =>
-        render(<PollListItemEnded {...props} onClick={jest.fn()} />);
+        render(<PollListItemEnded {...props} onClick={jest.fn()} />, { wrapper: TooltipProvider });
 
     beforeAll(() => {
         // mock default locale to en-GB and set timezone
diff --git a/test/components/views/right_panel/RoomSummaryCard-test.tsx b/test/components/views/right_panel/RoomSummaryCard-test.tsx
index 735b190b7f..f26c4e6037 100644
--- a/test/components/views/right_panel/RoomSummaryCard-test.tsx
+++ b/test/components/views/right_panel/RoomSummaryCard-test.tsx
@@ -18,6 +18,7 @@ import React from "react";
 import { render, fireEvent, screen } from "@testing-library/react";
 import { EventType, MatrixEvent, Room, MatrixClient, JoinRule } from "matrix-js-sdk/src/matrix";
 import { mocked, MockedObject } from "jest-mock";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import DMRoomMap from "../../../../src/utils/DMRoomMap";
 import RoomSummaryCard from "../../../../src/components/views/right_panel/RoomSummaryCard";
@@ -55,7 +56,9 @@ describe("<RoomSummaryCard />", () => {
 
         return render(<RoomSummaryCard {...defaultProps} {...props} />, {
             wrapper: ({ children }) => (
-                <MatrixClientContext.Provider value={mockClient}>{children}</MatrixClientContext.Provider>
+                <MatrixClientContext.Provider value={mockClient}>
+                    <TooltipProvider>{children}</TooltipProvider>
+                </MatrixClientContext.Provider>
             ),
         });
     };
diff --git a/test/components/views/right_panel/UserInfo-test.tsx b/test/components/views/right_panel/UserInfo-test.tsx
index 1336a295a0..eddba91f6a 100644
--- a/test/components/views/right_panel/UserInfo-test.tsx
+++ b/test/components/views/right_panel/UserInfo-test.tsx
@@ -37,6 +37,7 @@ import {
 import { defer } from "matrix-js-sdk/src/utils";
 import { EventEmitter } from "events";
 import { UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import UserInfo, {
     BanToggleButton,
@@ -195,7 +196,11 @@ describe("<UserInfo />", () => {
 
     const renderComponent = (props = {}) => {
         const Wrapper = (wrapperProps = {}) => {
-            return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
+            return (
+                <TooltipProvider>
+                    <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />
+                </TooltipProvider>
+            );
         };
 
         return render(<UserInfo {...defaultProps} {...props} />, {
@@ -412,7 +417,11 @@ describe("<UserInfoHeader />", () => {
 
     const renderComponent = (props = {}) => {
         const Wrapper = (wrapperProps = {}) => {
-            return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
+            return (
+                <TooltipProvider>
+                    <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />
+                </TooltipProvider>
+            );
         };
 
         return render(<UserInfoHeader {...defaultProps} {...props} />, {
diff --git a/test/components/views/rooms/EventTile-test.tsx b/test/components/views/rooms/EventTile-test.tsx
index ddc0b8c2b0..5e3442be3b 100644
--- a/test/components/views/rooms/EventTile-test.tsx
+++ b/test/components/views/rooms/EventTile-test.tsx
@@ -30,6 +30,7 @@ import {
 } from "matrix-js-sdk/src/matrix";
 import { EventEncryptionInfo, EventShieldColour, EventShieldReason } from "matrix-js-sdk/src/crypto-api";
 import { CryptoBackend } from "matrix-js-sdk/src/common-crypto/CryptoBackend";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import EventTile, { EventTileProps } from "../../../../src/components/views/rooms/EventTile";
 import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
@@ -66,11 +67,13 @@ describe("EventTile", () => {
         return (
             <MatrixClientContext.Provider value={client}>
                 <RoomContext.Provider value={props.roomContext}>
-                    <EventTile
-                        mxEvent={mxEvent}
-                        replacingEventId={mxEvent.replacingEventId()}
-                        {...(props.eventTilePropertyOverrides ?? {})}
-                    />
+                    <TooltipProvider>
+                        <EventTile
+                            mxEvent={mxEvent}
+                            replacingEventId={mxEvent.replacingEventId()}
+                            {...(props.eventTilePropertyOverrides ?? {})}
+                        />
+                    </TooltipProvider>
                 </RoomContext.Provider>
             </MatrixClientContext.Provider>
         );
diff --git a/test/components/views/rooms/LegacyRoomHeader-test.tsx b/test/components/views/rooms/LegacyRoomHeader-test.tsx
index 575f1ddf1a..ed82f74630 100644
--- a/test/components/views/rooms/LegacyRoomHeader-test.tsx
+++ b/test/components/views/rooms/LegacyRoomHeader-test.tsx
@@ -30,6 +30,7 @@ import { ClientWidgetApi, Widget } from "matrix-widget-api";
 import EventEmitter from "events";
 import { setupJestCanvasMock } from "jest-canvas-mock";
 import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import type { MatrixClient, MatrixEvent, RoomMember } from "matrix-js-sdk/src/matrix";
 import type { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
@@ -219,6 +220,7 @@ describe("LegacyRoomHeader", () => {
                     {...props}
                 />
             </RoomContext.Provider>,
+            { wrapper: TooltipProvider },
         );
     };
 
@@ -843,6 +845,7 @@ function mountHeader(room: Room, propsOverride = {}, roomContext?: Partial<IRoom
         <RoomContext.Provider value={{ ...roomContext, room } as IRoomState}>
             <RoomHeader {...props} />
         </RoomContext.Provider>,
+        { wrapper: TooltipProvider },
     );
 }
 
diff --git a/test/components/views/rooms/MemberList-test.tsx b/test/components/views/rooms/MemberList-test.tsx
index 586f58622e..fe8f5e3314 100644
--- a/test/components/views/rooms/MemberList-test.tsx
+++ b/test/components/views/rooms/MemberList-test.tsx
@@ -20,6 +20,7 @@ import { act, fireEvent, render, RenderResult, screen } from "@testing-library/r
 import { Room, MatrixClient, RoomState, RoomMember, User, MatrixEvent } from "matrix-js-sdk/src/matrix";
 import { compare } from "matrix-js-sdk/src/utils";
 import { mocked, MockedObject } from "jest-mock";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
 import * as TestUtils from "../../../test-utils";
@@ -229,6 +230,7 @@ describe("MemberList", () => {
                     ref={gatherWrappedRef}
                 />
             </SDKContext.Provider>,
+            { wrapper: TooltipProvider },
         );
     }
 
@@ -380,6 +382,7 @@ describe("MemberList", () => {
                             roomId={room.roomId}
                         />
                     </SDKContext.Provider>,
+                    { wrapper: TooltipProvider },
                 );
             };
 
diff --git a/test/components/views/rooms/MemberTile-test.tsx b/test/components/views/rooms/MemberTile-test.tsx
index 5f96147a50..555d9f8908 100644
--- a/test/components/views/rooms/MemberTile-test.tsx
+++ b/test/components/views/rooms/MemberTile-test.tsx
@@ -21,6 +21,7 @@ import { MatrixClient, RoomMember, Device } from "matrix-js-sdk/src/matrix";
 import { UserVerificationStatus, DeviceVerificationStatus } from "matrix-js-sdk/src/crypto-api";
 import { mocked } from "jest-mock";
 import userEvent from "@testing-library/user-event";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import * as TestUtils from "../../../test-utils";
 import MemberTile from "../../../../src/components/views/rooms/MemberTile";
@@ -36,7 +37,7 @@ describe("MemberTile", () => {
     });
 
     it("should not display an E2EIcon when the e2E status = normal", () => {
-        const { container } = render(<MemberTile member={member} />);
+        const { container } = render(<MemberTile member={member} />, { wrapper: TooltipProvider });
 
         expect(container).toMatchSnapshot();
     });
@@ -47,7 +48,7 @@ describe("MemberTile", () => {
             wasCrossSigningVerified: jest.fn().mockReturnValue(true),
         } as unknown as UserVerificationStatus);
 
-        const { container } = render(<MemberTile member={member} />);
+        const { container } = render(<MemberTile member={member} />, { wrapper: TooltipProvider });
 
         expect(container).toMatchSnapshot();
         await waitFor(async () => {
@@ -71,7 +72,7 @@ describe("MemberTile", () => {
             crossSigningVerified: true,
         } as DeviceVerificationStatus);
 
-        const { container } = render(<MemberTile member={member} />);
+        const { container } = render(<MemberTile member={member} />, { wrapper: TooltipProvider });
 
         expect(container).toMatchSnapshot();
         await waitFor(async () => {
diff --git a/test/components/views/rooms/MessageComposer-test.tsx b/test/components/views/rooms/MessageComposer-test.tsx
index 1aea150a8c..f8c6fa159a 100644
--- a/test/components/views/rooms/MessageComposer-test.tsx
+++ b/test/components/views/rooms/MessageComposer-test.tsx
@@ -18,6 +18,7 @@ import * as React from "react";
 import { EventType, MatrixEvent, Room, RoomMember, THREAD_RELATION_TYPE } from "matrix-js-sdk/src/matrix";
 import { act, render, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import {
     clearAllModals,
@@ -513,6 +514,7 @@ function wrapAndRender(
                     <MessageComposer {...defaultProps} {...props} />
                 </RoomContext.Provider>
             </MatrixClientContext.Provider>,
+            { wrapper: TooltipProvider },
         ),
         roomContext,
     };
diff --git a/test/components/views/rooms/RoomHeader-test.tsx b/test/components/views/rooms/RoomHeader-test.tsx
index ffda789c55..e2a54ae77c 100644
--- a/test/components/views/rooms/RoomHeader-test.tsx
+++ b/test/components/views/rooms/RoomHeader-test.tsx
@@ -33,12 +33,14 @@ import {
     getByRole,
     getByText,
     render,
+    RenderOptions,
     screen,
     waitFor,
 } from "@testing-library/react";
 import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";
+import { TooltipProvider } from "@vector-im/compound-web";
 
-import { filterConsole, mkEvent, stubClient, withClientContextRenderOptions } from "../../../test-utils";
+import { filterConsole, mkEvent, stubClient } from "../../../test-utils";
 import RoomHeader from "../../../../src/components/views/rooms/RoomHeader";
 import DMRoomMap from "../../../../src/utils/DMRoomMap";
 import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
@@ -52,9 +54,22 @@ import { CallStore } from "../../../../src/stores/CallStore";
 import { Call, ElementCall } from "../../../../src/models/Call";
 import * as ShieldUtils from "../../../../src/utils/ShieldUtils";
 import { Container, WidgetLayoutStore } from "../../../../src/stores/widgets/WidgetLayoutStore";
+import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
 
 jest.mock("../../../../src/utils/ShieldUtils");
 
+function getWrapper(): RenderOptions {
+    return {
+        wrapper: ({ children }) => (
+            <TooltipProvider>
+                <MatrixClientContext.Provider value={MatrixClientPeg.safeGet()}>
+                    {children}
+                </MatrixClientContext.Provider>
+            </TooltipProvider>
+        ),
+    };
+}
+
 describe("RoomHeader", () => {
     filterConsole(
         "[getType] Room !1:example.org does not have an m.room.create event",
@@ -84,10 +99,7 @@ describe("RoomHeader", () => {
     });
 
     it("renders the room header", () => {
-        const { container } = render(
-            <RoomHeader room={room} />,
-            withClientContextRenderOptions(MatrixClientPeg.get()!),
-        );
+        const { container } = render(<RoomHeader room={room} />, getWrapper());
         expect(container).toHaveTextContent(ROOM_ID);
     });
 
@@ -105,19 +117,13 @@ describe("RoomHeader", () => {
         });
         await room.addLiveEvents([roomTopic]);
 
-        const { container } = render(
-            <RoomHeader room={room} />,
-            withClientContextRenderOptions(MatrixClientPeg.get()!),
-        );
+        const { container } = render(<RoomHeader room={room} />, getWrapper());
         expect(container).toHaveTextContent(TOPIC);
         expect(getByRole(container, "link")).toHaveTextContent("http://element.io");
     });
 
     it("opens the room summary", async () => {
-        const { container } = render(
-            <RoomHeader room={room} />,
-            withClientContextRenderOptions(MatrixClientPeg.get()!),
-        );
+        const { container } = render(<RoomHeader room={room} />, getWrapper());
 
         fireEvent.click(getByText(container, ROOM_ID));
         expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.RoomSummary });
@@ -149,10 +155,7 @@ describe("RoomHeader", () => {
             },
         ]);
 
-        const { asFragment } = render(
-            <RoomHeader room={room} />,
-            withClientContextRenderOptions(MatrixClientPeg.get()!),
-        );
+        const { asFragment } = render(<RoomHeader room={room} />, getWrapper());
 
         expect(asFragment()).toMatchSnapshot();
     });
@@ -199,10 +202,7 @@ describe("RoomHeader", () => {
         room.currentState.setJoinedMemberCount(members.length);
         room.getJoinedMembers = jest.fn().mockReturnValue(members);
 
-        const { container } = render(
-            <RoomHeader room={room} />,
-            withClientContextRenderOptions(MatrixClientPeg.get()!),
-        );
+        const { container } = render(<RoomHeader room={room} />, getWrapper());
 
         expect(container).toHaveTextContent("4");
 
@@ -215,10 +215,7 @@ describe("RoomHeader", () => {
     });
 
     it("opens the thread panel", async () => {
-        const { container } = render(
-            <RoomHeader room={room} />,
-            withClientContextRenderOptions(MatrixClientPeg.get()!),
-        );
+        const { container } = render(<RoomHeader room={room} />, getWrapper());
 
         fireEvent.click(getByLabelText(container, "Threads"));
         expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.ThreadPanel });
@@ -229,10 +226,7 @@ describe("RoomHeader", () => {
             if (name === "feature_notifications") return true;
         });
 
-        const { container } = render(
-            <RoomHeader room={room} />,
-            withClientContextRenderOptions(MatrixClientPeg.get()!),
-        );
+        const { container } = render(<RoomHeader room={room} />, getWrapper());
 
         fireEvent.click(getByLabelText(container, "Notifications"));
         expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.NotificationPanel });
@@ -241,10 +235,7 @@ describe("RoomHeader", () => {
     describe("groups call disabled", () => {
         it("you can't call if you're alone", () => {
             mockRoomMembers(room, 1);
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
             for (const button of getAllByLabelText(container, "There's no one here to call")) {
                 expect(button).toHaveAttribute("aria-disabled", "true");
             }
@@ -252,10 +243,7 @@ describe("RoomHeader", () => {
 
         it("you can call when you're two in the room", async () => {
             mockRoomMembers(room, 2);
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
             const voiceButton = getByLabelText(container, "Voice call");
             const videoButton = getByLabelText(container, "Video call");
             expect(voiceButton).not.toHaveAttribute("aria-disabled", "true");
@@ -276,10 +264,7 @@ describe("RoomHeader", () => {
                 // The JS-SDK does not export the class `MatrixCall` only the type
                 {} as MatrixCall,
             );
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
             for (const button of getAllByLabelText(container, "Ongoing call")) {
                 expect(button).toHaveAttribute("aria-disabled", "true");
             }
@@ -288,10 +273,7 @@ describe("RoomHeader", () => {
         it("can calls in large rooms if able to edit widgets", () => {
             mockRoomMembers(room, 10);
             jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
 
             expect(getByLabelText(container, "Voice call")).not.toHaveAttribute("aria-disabled", "true");
             expect(getByLabelText(container, "Video call")).not.toHaveAttribute("aria-disabled", "true");
@@ -300,10 +282,7 @@ describe("RoomHeader", () => {
         it("disable calls in large rooms by default", () => {
             mockRoomMembers(room, 10);
             jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(false);
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
             expect(
                 getByLabelText(container, "You do not have permission to start voice calls", { selector: "button" }),
             ).toHaveAttribute("aria-disabled", "true");
@@ -324,10 +303,7 @@ describe("RoomHeader", () => {
             // allow element calls
             jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
 
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
 
             expect(screen.queryByTitle("Voice call")).toBeNull();
 
@@ -349,10 +325,7 @@ describe("RoomHeader", () => {
 
             jest.spyOn(CallStore.instance, "getCall").mockReturnValue({ widget: {} } as Call);
 
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
             expect(getByLabelText(container, "Ongoing call")).toHaveAttribute("aria-disabled", "true");
         });
 
@@ -366,10 +339,7 @@ describe("RoomHeader", () => {
             const widget = {};
             jest.spyOn(CallStore.instance, "getCall").mockReturnValue({ widget } as Call);
 
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
             expect(getByLabelText(container, "Video call")).not.toHaveAttribute("aria-disabled", "true");
             fireEvent.click(getByLabelText(container, "Video call"));
             expect(spy).toHaveBeenCalledWith(room, widget, Container.Top);
@@ -381,10 +351,7 @@ describe("RoomHeader", () => {
                 // The JS-SDK does not export the class `MatrixCall` only the type
                 {} as MatrixCall,
             );
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
             for (const button of getAllByLabelText(container, "Ongoing call")) {
                 expect(button).toHaveAttribute("aria-disabled", "true");
             }
@@ -392,10 +359,7 @@ describe("RoomHeader", () => {
 
         it("can't call if you have no friends", () => {
             mockRoomMembers(room, 1);
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
             for (const button of getAllByLabelText(container, "There's no one here to call")) {
                 expect(button).toHaveAttribute("aria-disabled", "true");
             }
@@ -403,10 +367,7 @@ describe("RoomHeader", () => {
 
         it("calls using legacy or jitsi", async () => {
             mockRoomMembers(room, 2);
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
 
             const voiceButton = getByLabelText(container, "Voice call");
             const videoButton = getByLabelText(container, "Video call");
@@ -429,10 +390,7 @@ describe("RoomHeader", () => {
                 return false;
             });
 
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
 
             const voiceButton = getByLabelText(container, "Voice call");
             const videoButton = getByLabelText(container, "Video call");
@@ -456,10 +414,7 @@ describe("RoomHeader", () => {
                 return false;
             });
 
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
 
             const voiceButton = getByLabelText(container, "Voice call");
             const videoButton = getByLabelText(container, "Video call");
@@ -483,10 +438,7 @@ describe("RoomHeader", () => {
             });
             room.addLiveEvents([joinRuleEvent]);
 
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
 
             expect(getByLabelText(container, "Public room")).toBeInTheDocument();
         });
@@ -521,10 +473,7 @@ describe("RoomHeader", () => {
         ])("shows the %s icon", async (value: ShieldUtils.E2EStatus, expectedLabel: string) => {
             jest.spyOn(ShieldUtils, "shieldStatusForRoom").mockResolvedValue(value);
 
-            const { container } = render(
-                <RoomHeader room={room} />,
-                withClientContextRenderOptions(MatrixClientPeg.get()!),
-            );
+            const { container } = render(<RoomHeader room={room} />, getWrapper());
 
             await waitFor(() => expect(getByLabelText(container, expectedLabel)).toBeInTheDocument());
         });
@@ -539,10 +488,7 @@ describe("RoomHeader", () => {
                 onClick: () => {},
             },
         ];
-        render(
-            <RoomHeader room={room} additionalButtons={additionalButtons} />,
-            withClientContextRenderOptions(MatrixClientPeg.get()!),
-        );
+        render(<RoomHeader room={room} additionalButtons={additionalButtons} />, getWrapper());
         expect(screen.getByRole("button", { name: "test-label" })).toBeInTheDocument();
     });
 
@@ -557,10 +503,7 @@ describe("RoomHeader", () => {
             },
         ];
 
-        render(
-            <RoomHeader room={room} additionalButtons={additionalButtons} />,
-            withClientContextRenderOptions(MatrixClientPeg.get()!),
-        );
+        render(<RoomHeader room={room} additionalButtons={additionalButtons} />, getWrapper());
 
         const button = screen.getByRole("button", { name: "test-label" });
         const event = createEvent.click(button);
@@ -573,7 +516,7 @@ describe("RoomHeader", () => {
 
     describe("ask to join disabled", () => {
         it("does not render the RoomKnocksBar", () => {
-            render(<RoomHeader room={room} />, withClientContextRenderOptions(MatrixClientPeg.get()!));
+            render(<RoomHeader room={room} />, getWrapper());
             expect(screen.queryByRole("heading", { name: "Asking to join" })).not.toBeInTheDocument();
         });
     });
@@ -585,7 +528,7 @@ describe("RoomHeader", () => {
             jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Knock);
             jest.spyOn(room, "getMembersWithMembership").mockReturnValue([new RoomMember(room.roomId, "@foo")]);
 
-            render(<RoomHeader room={room} />, withClientContextRenderOptions(MatrixClientPeg.get()!));
+            render(<RoomHeader room={room} />, getWrapper());
             expect(screen.getByRole("heading", { name: "Asking to join" })).toBeInTheDocument();
         });
     });
diff --git a/test/components/views/rooms/RoomHeader/VideoRoomChatButton-test.tsx b/test/components/views/rooms/RoomHeader/VideoRoomChatButton-test.tsx
index 3f8756028d..b515a1f87a 100644
--- a/test/components/views/rooms/RoomHeader/VideoRoomChatButton-test.tsx
+++ b/test/components/views/rooms/RoomHeader/VideoRoomChatButton-test.tsx
@@ -18,6 +18,7 @@ import React from "react";
 import { MockedObject } from "jest-mock";
 import { Room } from "matrix-js-sdk/src/matrix";
 import { fireEvent, render, screen } from "@testing-library/react";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { VideoRoomChatButton } from "../../../../../src/components/views/rooms/RoomHeader/VideoRoomChatButton";
 import { SDKContext, SdkContextClass } from "../../../../../src/contexts/SDKContext";
@@ -56,7 +57,11 @@ describe("<VideoRoomChatButton />", () => {
 
     const getComponent = (room: Room) =>
         render(<VideoRoomChatButton room={room} />, {
-            wrapper: ({ children }) => <SDKContext.Provider value={sdkContext}>{children}</SDKContext.Provider>,
+            wrapper: ({ children }) => (
+                <SDKContext.Provider value={sdkContext}>
+                    <TooltipProvider>{children}</TooltipProvider>
+                </SDKContext.Provider>
+            ),
         });
 
     beforeEach(() => {
diff --git a/test/components/views/settings/devices/FilteredDeviceList-test.tsx b/test/components/views/settings/devices/FilteredDeviceList-test.tsx
index c37ea61228..53d24de422 100644
--- a/test/components/views/settings/devices/FilteredDeviceList-test.tsx
+++ b/test/components/views/settings/devices/FilteredDeviceList-test.tsx
@@ -16,6 +16,7 @@ limitations under the License.
 
 import React, { ComponentProps } from "react";
 import { act, fireEvent, render } from "@testing-library/react";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import { FilteredDeviceList } from "../../../../../src/components/views/settings/devices/FilteredDeviceList";
 import { DeviceSecurityVariation } from "../../../../../src/components/views/settings/devices/types";
@@ -81,7 +82,11 @@ describe("<FilteredDeviceList />", () => {
         supportsMSC3881: true,
     };
 
-    const getComponent = (props = {}) => <FilteredDeviceList {...defaultProps} {...props} />;
+    const getComponent = (props = {}) => (
+        <TooltipProvider>
+            <FilteredDeviceList {...defaultProps} {...props} />
+        </TooltipProvider>
+    );
 
     afterAll(() => {
         jest.spyOn(global.Date, "now").mockRestore();
diff --git a/test/components/views/settings/devices/FilteredDeviceListHeader-test.tsx b/test/components/views/settings/devices/FilteredDeviceListHeader-test.tsx
index 05380493d3..18649a3f49 100644
--- a/test/components/views/settings/devices/FilteredDeviceListHeader-test.tsx
+++ b/test/components/views/settings/devices/FilteredDeviceListHeader-test.tsx
@@ -16,6 +16,7 @@ limitations under the License.
 
 import { fireEvent, render } from "@testing-library/react";
 import React from "react";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import FilteredDeviceListHeader from "../../../../../src/components/views/settings/devices/FilteredDeviceListHeader";
 
@@ -27,7 +28,11 @@ describe("<FilteredDeviceListHeader />", () => {
         children: <div>test</div>,
         ["data-testid"]: "test123",
     };
-    const getComponent = (props = {}) => <FilteredDeviceListHeader {...defaultProps} {...props} />;
+    const getComponent = (props = {}) => (
+        <TooltipProvider>
+            <FilteredDeviceListHeader {...defaultProps} {...props} />
+        </TooltipProvider>
+    );
 
     it("renders correctly when no devices are selected", () => {
         const { container } = render(getComponent());
diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
index 636d369344..f1fcb88dee 100644
--- a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
+++ b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
@@ -35,6 +35,7 @@ import {
     MatrixClient,
 } from "matrix-js-sdk/src/matrix";
 import { mocked, MockedObject } from "jest-mock";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import {
     clearAllModals,
@@ -97,9 +98,11 @@ describe("<SessionManagerTab />", () => {
 
     const defaultProps = {};
     const getComponent = (props = {}): React.ReactElement => (
-        <SDKContext.Provider value={sdkContext}>
-            <SessionManagerTab {...defaultProps} {...props} />
-        </SDKContext.Provider>
+        <TooltipProvider>
+            <SDKContext.Provider value={sdkContext}>
+                <SessionManagerTab {...defaultProps} {...props} />
+            </SDKContext.Provider>
+        </TooltipProvider>
     );
 
     const toggleDeviceDetails = (
diff --git a/test/components/views/voip/CallView-test.tsx b/test/components/views/voip/CallView-test.tsx
index b1a672189f..7e7f8ee9cc 100644
--- a/test/components/views/voip/CallView-test.tsx
+++ b/test/components/views/voip/CallView-test.tsx
@@ -20,6 +20,7 @@ import { render, screen, act, fireEvent, waitFor, cleanup } from "@testing-libra
 import { mocked, Mocked } from "jest-mock";
 import { MatrixClient, PendingEventOrdering, Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
 import { Widget } from "matrix-widget-api";
+import { TooltipProvider } from "@vector-im/compound-web";
 
 import type { RoomMember } from "matrix-js-sdk/src/matrix";
 import type { ClientWidgetApi } from "matrix-widget-api";
@@ -75,7 +76,7 @@ describe("CallView", () => {
     });
 
     const renderView = async (): Promise<void> => {
-        render(<CallView room={room} resizing={false} waitForCall={false} />);
+        render(<CallView room={room} resizing={false} waitForCall={false} />, { wrapper: TooltipProvider });
         await act(() => Promise.resolve()); // Let effects settle
     };
 
diff --git a/yarn.lock b/yarn.lock
index 0897adc562..f7fe8931f7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3362,10 +3362,10 @@
   dependencies:
     svg2vectordrawable "^2.9.1"
 
-"@vector-im/compound-web@1.0.0":
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-1.0.0.tgz#cee4a1c1d7df50951ebb1c167bfab26311b68d5d"
-  integrity sha512-TwohQjgQilndkg+PTfRH8s98onHOj9MdmrLEObozN3oLZ0Awx0LsyQf/+tcm2ZwpCotWuP9DEzuRXOQgSU4OLg==
+"@vector-im/compound-web@2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-2.0.0.tgz#9ffc621f32be11acabe74bb3ff59cc4d8bc845ac"
+  integrity sha512-vEhGayoBSq4WLf86VmFoX9h0KIxaAjlG+kmcJLWitsqnPEDOG0XPhScYqzEshFqdJFLWX6gBOnXYLeq065t57w==
   dependencies:
     "@radix-ui/react-context-menu" "^2.1.5"
     "@radix-ui/react-dropdown-menu" "^2.0.6"