Propel

Context Menu

A menu of actions that opens at the pointer on right click or long press.

Basic

Right-click here
Show code
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuTrigger,
} from "@makeplane/propel/components/context-menu";
import { Icon } from "@makeplane/propel/components/icon";
import { Shortcut } from "@makeplane/propel/components/shortcut";
import { ClipboardPaste, Copy, Scissors, Trash2 } from "lucide-react";

export default function BasicDemo() {
  return (
    <ContextMenu>
      <ContextMenuTrigger render={<div />}>Right-click here</ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem
          tone="neutral"
          icon={<Icon icon={Scissors} />}
          aria-keyshortcuts="Meta+X"
          endContent={<Shortcut keys="⌘X" />}
          label="Cut"
        />
        <ContextMenuItem
          tone="neutral"
          icon={<Icon icon={Copy} />}
          aria-keyshortcuts="Meta+C"
          endContent={<Shortcut keys="⌘C" />}
          label="Copy"
        />
        <ContextMenuItem
          tone="neutral"
          icon={<Icon icon={ClipboardPaste} />}
          aria-keyshortcuts="Meta+V"
          endContent={<Shortcut keys="⌘V" />}
          label="Paste"
        />
        <ContextMenuSeparator />
        <ContextMenuItem tone="danger" icon={<Icon icon={Trash2} />} label="Delete" />
      </ContextMenuContent>
    </ContextMenu>
  );
}

Nest a ContextMenuSubmenu to reveal more destinations beside the parent menu.

Right-click here
Show code
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuSubmenu,
  ContextMenuSubmenuTrigger,
  ContextMenuTrigger,
} from "@makeplane/propel/components/context-menu";
import { Icon } from "@makeplane/propel/components/icon";
import { Copy, FolderInput, PencilLine, Trash2 } from "lucide-react";

export default function SubmenuDemo() {
  return (
    <ContextMenu>
      <ContextMenuTrigger render={<div />}>Right-click here</ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem tone="neutral" icon={<Icon icon={PencilLine} />} label="Rename" />
        <ContextMenuItem tone="neutral" icon={<Icon icon={Copy} />} label="Duplicate" />
        <ContextMenuSubmenu>
          <ContextMenuSubmenuTrigger
            tone="neutral"
            icon={<Icon icon={FolderInput} />}
            label="Move to"
          />
          <ContextMenuContent>
            <ContextMenuItem tone="neutral" label="Mobile app" />
            <ContextMenuItem tone="neutral" label="Web app" />
            <ContextMenuItem tone="neutral" label="Design system" />
          </ContextMenuContent>
        </ContextMenuSubmenu>
        <ContextMenuSeparator />
        <ContextMenuItem tone="danger" icon={<Icon icon={Trash2} />} label="Delete" />
      </ContextMenuContent>
    </ContextMenu>
  );
}

ContextMenuLinkItem renders a real anchor, so a row can navigate while keeping menu-item behavior.

Right-click here
Show code
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuLinkItem,
  ContextMenuSeparator,
  ContextMenuTrigger,
} from "@makeplane/propel/components/context-menu";
import { Icon } from "@makeplane/propel/components/icon";
import { Shortcut } from "@makeplane/propel/components/shortcut";
import { Copy, ExternalLink, Link2 } from "lucide-react";

export default function LinkItemsDemo() {
  return (
    <ContextMenu>
      <ContextMenuTrigger render={<div />}>Right-click here</ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuLinkItem
          tone="neutral"
          icon={<Icon icon={ExternalLink} />}
          href="#work-item"
          target="_blank"
          rel="noopener noreferrer"
          label="Open in new tab"
        />
        <ContextMenuLinkItem
          tone="neutral"
          icon={<Icon icon={Link2} />}
          href="#activity"
          label="Go to activity"
        />
        <ContextMenuSeparator />
        <ContextMenuItem
          tone="neutral"
          icon={<Icon icon={Copy} />}
          aria-keyshortcuts="Meta+C"
          endContent={<Shortcut keys="⌘C" />}
          label="Copy link"
        />
      </ContextMenuContent>
    </ContextMenu>
  );
}

Selection rows

Right-click for view options
Show code
import {
  ContextMenu,
  ContextMenuCheckboxItem,
  ContextMenuContent,
  ContextMenuRadioGroup,
  ContextMenuRadioItem,
  ContextMenuSeparator,
  ContextMenuTrigger,
} from "@makeplane/propel/components/context-menu";
import * as React from "react";

export default function SelectionRowsDemo() {
  const [wrap, setWrap] = React.useState(true);
  const [sort, setSort] = React.useState("manual");

  return (
    <ContextMenu>
      <ContextMenuTrigger render={<div />}>Right-click for view options</ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuCheckboxItem
          tone="neutral"
          checked={wrap}
          onCheckedChange={setWrap}
          closeOnClick={false}
          label="Wrap titles"
        />
        <ContextMenuSeparator />
        <ContextMenuRadioGroup value={sort} onValueChange={setSort}>
          <ContextMenuRadioItem
            tone="neutral"
            value="manual"
            closeOnClick={false}
            label="Manual order"
          />
          <ContextMenuRadioItem
            tone="neutral"
            value="updated"
            closeOnClick={false}
            label="Last updated"
          />
        </ContextMenuRadioGroup>
      </ContextMenuContent>
    </ContextMenu>
  );
}

Installation

import {
  ContextMenu,
  ContextMenuTrigger,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
} from "@makeplane/propel/components/context-menu";

Props

ContextMenu

PropTypeRequiredDescription
childrenReactNode | PayloadChildRenderFunction<unknown>NoThe content of the popover. This can be a regular React node or a render function that receives the `payload` of the active trigger.

ContextMenuItem

PropTypeRequiredDescription
tone"neutral" | "danger"YesNeutral rows use the standard text hierarchy; `danger` rows use the error palette.
iconReactNodeNoLeading element before the label, e.g. `<Icon icon={Copy} />`.
labelstringYesPrimary row label.
endContentReactElement<unknown, string | JSXElementConstructor<any>>NoElement rendered at the inline end of the row, e.g. `<Shortcut keys="⌘ K" />`.
selectedbooleanNoSingle-select selected state.