ContextMenu

January 28, 2026 ยท View on GitHub

Context menu component for displaying contextual actions. Supports submenus, headers, separators, toggles, badges, hotkeys, and mobile-responsive layouts.

Usage

import { ContextMenu } from "@docspace/ui-kit/components/context-menu";
import type { ContextMenuModel, ContextMenuRefType } from "@docspace/ui-kit/components/context-menu";

const menuRef = useRef<ContextMenuRefType>(null);

const model: ContextMenuModel[] = [
  { key: "edit", label: "Edit", icon: editIcon, onClick: handleEdit },
  { key: "copy", label: "Copy", icon: copyIcon, onClick: handleCopy },
  { key: "sep1", isSeparator: true },
  { key: "delete", label: "Delete", icon: deleteIcon, onClick: handleDelete },
];

<ContextMenu ref={menuRef} model={model} />

// Show menu programmatically
const handleRightClick = (e: React.MouseEvent) => {
  menuRef.current?.show(e);
};

Props

PropTypeDefaultDescription
modelContextMenuModel[]-Array of menu items
headerHeaderType-Header with icon, title, and optional avatar
idstring"contextMenu"Unique identifier
classNamestring-Additional CSS class
styleCSSProperties-Inline styles
globalboolean-Attach menu to document
withBackdropbooleantrueShow backdrop overlay
ignoreChangeViewboolean-Ignore mobile view restrictions
autoZIndexboolean-Automatic z-index layering
baseZIndexnumber-Base z-index value
appendToHTMLElement-DOM element to mount menu
onShow(e) => void-Callback when menu shows
onHide(e) => void-Callback when menu hides
containerRefRefObject<HTMLDivElement>-Reference to container
scaledboolean-Scale width by container
fillIconboolean-Fill icons with default colors
getContextModel() => ContextMenuModel[]-Dynamic model getter
leftOffsetnumber-Left position offset
rightOffsetnumber-Right position offset
isRoomboolean-Room context styling
isArchiveboolean-Archive context styling
badgeUrlstring-Badge icon URL
headerOnlyMobileboolean-Show header only on mobile
maxHeightLowerSubmenunumber-Max height for lower submenus
showDisabledItemsboolean-Show disabled items
withHotkeysboolean-Enable keyboard navigation
withoutBackHeaderButtonboolean-Hide back button in header
dataTestIdstring-Test ID

Types

ContextMenuModel

type ContextMenuModel = ContextMenuType | SeparatorType;

ContextMenuType

type ContextMenuType = {
  key: string | number;
  label: string | ReactNode;
  icon?: string;
  disabled?: boolean;
  onClick?: (value, item?) => void;
  items?: ContextMenuModel[];       // Submenu items
  url?: string;                      // External link
  target?: string;                   // Link target
  isHeader?: boolean;                // Header item
  isLoader?: boolean;                // Show loader
  onLoad?: () => Promise<ContextMenuModel[]>;  // Async load
  withToggle?: boolean;              // Toggle switch
  checked?: boolean;                 // Toggle state
  badgeLabel?: string;               // Badge text
  isPaidBadge?: boolean;             // Paid feature badge
  className?: string;
  style?: CSSProperties;
  dataTestId?: string;
};

SeparatorType

type SeparatorType = {
  key: string | number;
  isSeparator: true;
  disabled?: boolean;
};

HeaderType

type HeaderType = {
  title: string;
  icon?: string;
  avatar?: string;
  logo?: string;
  badgeUrl?: string;
};

ContextMenuRefType

type ContextMenuRefType = {
  show: (e: MouseEvent) => void;
  hide: (e: MouseEvent | Event) => void;
  toggle: (e: MouseEvent | Event) => boolean | undefined;
  menuRef: RefObject<HTMLDivElement>;
};

CSS Variables

The component uses CSS variables for theming, defined locally within the component:

VariableLightDarkDescription
--context-menu-background#ffffff#333333Menu background
--context-menu-bordernone1px solid #474747Menu border
--context-menu-header-border1px solid #eceef11px solid #474747Header border
--context-menu-box-shadow0px 8px 16px 0px rgba(...)0px 8px 16px 0px rgba(...)Box shadow
--context-menu-header-text-color#333333#ffffffHeader text color
--context-menu-header-text-margin0 0 0 8px0 0 0 8pxHeader text margin
--context-menu-submenu-list-margin4px4pxSubmenu margin
--context-menu-button-border#d0d5da#858585Button border
--context-menu-button-hover-border#a3a9ae#858585Button hover border
--sub-menu-item-background-color#ffffff#333333Item background
--sub-menu-item-hover-background-color#f8f9f9#3d3d3dItem hover background
--sub-menu-item-disabled-color#a3a9ae#a3a9aeDisabled item color
--sub-menu-item-disabled-background-color#ffffff#333333Disabled item background
--drop-down-item-hover-color#f8f9f9#3d3d3dDropdown item hover

Examples

Basic Menu

const model = [
  { key: "edit", label: "Edit", onClick: () => {} },
  { key: "copy", label: "Copy", onClick: () => {} },
  { key: "delete", label: "Delete", onClick: () => {} },
];

<ContextMenu model={model} />

With Icons

const model = [
  { key: "edit", label: "Edit", icon: editIcon, onClick: handleEdit },
  { key: "copy", label: "Copy", icon: copyIcon, onClick: handleCopy },
];

<ContextMenu model={model} fillIcon />

With Separators

const model = [
  { key: "edit", label: "Edit", onClick: handleEdit },
  { key: "copy", label: "Copy", onClick: handleCopy },
  { key: "sep", isSeparator: true },
  { key: "delete", label: "Delete", onClick: handleDelete },
];

<ContextMenu model={model} />

With Header

const header = {
  title: "Document.pdf",
  icon: "/icons/pdf.svg",
};

<ContextMenu model={model} header={header} />

With Submenu

const model = [
  { key: "edit", label: "Edit", onClick: handleEdit },
  {
    key: "share",
    label: "Share",
    items: [
      { key: "email", label: "Email", onClick: handleEmail },
      { key: "link", label: "Copy Link", onClick: handleLink },
    ],
  },
];

<ContextMenu model={model} />

With Toggle

const model = [
  {
    key: "notifications",
    label: "Notifications",
    withToggle: true,
    checked: isEnabled,
    onClick: () => setIsEnabled(!isEnabled),
  },
];

<ContextMenu model={model} />

With Badge

const model = [
  {
    key: "premium",
    label: "Premium Feature",
    badgeLabel: "PRO",
    isPaidBadge: true,
    onClick: handlePremium,
  },
];

<ContextMenu model={model} />

Programmatic Control

const menuRef = useRef<ContextMenuRefType>(null);

const handleContextMenu = (e: React.MouseEvent) => {
  e.preventDefault();
  menuRef.current?.show(e);
};

<div onContextMenu={handleContextMenu}>
  Right-click me
</div>

<ContextMenu ref={menuRef} model={model} />

With Hotkeys

<ContextMenu model={model} withHotkeys />

Async Loading

const model = [
  {
    key: "share",
    label: "Share with...",
    isLoader: true,
    onLoad: async () => {
      const users = await fetchUsers();
      return users.map(u => ({
        key: u.id,
        label: u.name,
        onClick: () => shareWith(u.id),
      }));
    },
  },
];

<ContextMenu model={model} />