diff --git a/web/app.react.js b/web/app.react.js
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -28,6 +28,7 @@
 import Chat from './chat/chat.react';
 import InputStateContainer from './input/input-state-container.react';
 import LoadingIndicator from './loading-indicator.react';
+import { MenuProvider } from './menu-provider.react';
 import { ModalProvider, useModalContext } from './modals/modal-provider.react';
 import DisconnectedBar from './redux/disconnected-bar';
 import DisconnectedBarVisibilityHandler from './redux/disconnected-bar-visibility-handler';
@@ -129,10 +130,12 @@
     }
     return (
       <DndProvider backend={HTML5Backend}>
-        <FocusHandler />
-        <VisibilityHandler />
-        {content}
-        {this.props.modal}
+        <MenuProvider>
+          <FocusHandler />
+          <VisibilityHandler />
+          {content}
+          {this.props.modal}
+        </MenuProvider>
       </DndProvider>
     );
   }
diff --git a/web/menu-provider.react.js b/web/menu-provider.react.js
new file mode 100644
--- /dev/null
+++ b/web/menu-provider.react.js
@@ -0,0 +1,84 @@
+// @flow
+
+import invariant from 'invariant';
+import * as React from 'react';
+
+import type { SetState } from 'lib/types/hook-types';
+
+import css from './menu.css';
+
+type MenuPosition = {
+  +top: number,
+  +left: number,
+};
+type Props = {
+  +children: React.Node,
+};
+type MenuContextType = {
+  +renderMenu: SetState<React.Node>,
+  +setMenuPosition: SetState<MenuPosition>,
+  +closeMenu: React.Node => void,
+  +currentOpenMenu: symbol,
+  +setCurrentOpenMenu: SetState<symbol>,
+};
+
+const MenuContext: React.Context<MenuContextType> = React.createContext<MenuContextType>(
+  {
+    renderMenu: () => {},
+    setMenuPosition: () => {},
+    closeMenu: () => {},
+    currentOpenMenu: Symbol(),
+    setCurrentOpenMenu: () => {},
+  },
+);
+
+function MenuProvider(props: Props): React.Node {
+  const { children } = props;
+  const [menu, setMenu] = React.useState(null);
+  const [currentOpenMenu, setCurrentOpenMenu] = React.useState<symbol>(
+    Symbol(),
+  );
+  const [position, setPosition] = React.useState<MenuPosition>({
+    top: 0,
+    left: 0,
+  });
+
+  const closeMenu = React.useCallback((menuToClose: React.Node) => {
+    setCurrentOpenMenu(Symbol());
+    setMenu(oldMenu => {
+      if (oldMenu === menuToClose) {
+        return null;
+      } else {
+        return oldMenu;
+      }
+    });
+  }, []);
+
+  const value = React.useMemo(
+    () => ({
+      renderMenu: setMenu,
+      setMenuPosition: setPosition,
+      closeMenu,
+      setCurrentOpenMenu,
+      currentOpenMenu,
+    }),
+    [closeMenu, currentOpenMenu],
+  );
+  return (
+    <>
+      <MenuContext.Provider value={value}>{children}</MenuContext.Provider>
+      <div style={position} className={css.container}>
+        {menu}
+      </div>
+    </>
+  );
+}
+
+function useRenderMenu(): MenuContextType {
+  const context = React.useContext(MenuContext);
+  invariant(context, 'MenuContext not found');
+
+  return context;
+}
+
+export { MenuProvider, useRenderMenu };
diff --git a/web/menu.css b/web/menu.css
new file mode 100644
--- /dev/null
+++ b/web/menu.css
@@ -0,0 +1,3 @@
+div.container {
+  position: absolute;
+}