diff --git a/web/chat/tooltip-provider.js b/web/chat/tooltip-provider.js
--- a/web/chat/tooltip-provider.js
+++ b/web/chat/tooltip-provider.js
@@ -5,6 +5,8 @@
 
 import type { TooltipPositionStyle } from './tooltip-utils';
 
+const onMouseLeaveSourceDisappearTimeoutMs = 200;
+
 export type RenderTooltipParams = {
   +newNode: React.Node,
   +tooltipPositionStyle: TooltipPositionStyle,
@@ -49,11 +51,48 @@
     setTooltipPosition,
   ] = React.useState<?TooltipPositionStyle>(null);
 
-  const clearCurrentTooltip = React.useCallback(() => {}, []);
+  const clearTooltip = React.useCallback((tooltipToClose: symbol) => {
+    if (tooltipSymbol.current !== tooltipToClose) {
+      return;
+    }
+    tooltipCancelTimer.current = null;
+    setTooltipNode(null);
+    setTooltipPosition(null);
+    tooltipSymbol.current = null;
+  }, []);
+
+  const clearCurrentTooltip = React.useCallback(() => {
+    if (tooltipSymbol.current) {
+      clearTooltip(tooltipSymbol.current);
+    }
+  }, [clearTooltip]);
 
   const renderTooltip = React.useCallback(
-    () => ({ onMouseLeaveCallback: () => {}, clearTooltip: () => {} }),
-    [],
+    ({
+      newNode,
+      tooltipPositionStyle: newTooltipPosition,
+    }: RenderTooltipParams): RenderTooltipResult => {
+      setTooltipNode(newNode);
+      setTooltipPosition(newTooltipPosition);
+      const newNodeSymbol = Symbol();
+      tooltipSymbol.current = newNodeSymbol;
+
+      if (tooltipCancelTimer.current) {
+        clearTimeout(tooltipCancelTimer.current);
+      }
+
+      return {
+        onMouseLeaveCallback: () => {
+          const newTimer = setTimeout(
+            () => clearTooltip(newNodeSymbol),
+            onMouseLeaveSourceDisappearTimeoutMs,
+          );
+          tooltipCancelTimer.current = newTimer;
+        },
+        clearTooltip: () => clearTooltip(newNodeSymbol),
+      };
+    },
+    [clearTooltip],
   );
 
   // eslint-disable-next-line no-unused-vars