Devie UI
Right click here

The ContextMenu component extends Base UI's ContextMenu. It displays a menu of contextual actions triggered by right-clicking (or long-pressing on touch devices) on an element.

Installation

Here is the component implementation code that you can copy to your project:

// https://base-ui.com/react/components/context-menu
 
import { ContextMenu as BaseContextMenu } from "@base-ui-components/react/context-menu";
import clsx from "clsx";
import { Check, ChevronRight, Circle } from "lucide-react";
import styles from "./ContextMenu.module.scss";
 
const Root = BaseContextMenu.Root;
 
interface TriggerProps extends BaseContextMenu.Trigger.Props {
  className?: string;
}
 
function Trigger({ className, ...props }: TriggerProps) {
  return (
    <BaseContextMenu.Trigger
      className={clsx(styles.trigger, className)}
      {...props}
    />
  );
}
 
const Portal = BaseContextMenu.Portal;
const Backdrop = BaseContextMenu.Backdrop;
 
interface PositionerProps extends BaseContextMenu.Positioner.Props {
  className?: string;
}
 
function Positioner({ className, ...props }: PositionerProps) {
  return (
    <BaseContextMenu.Positioner
      className={clsx(styles.positioner, className)}
      {...props}
    />
  );
}
 
interface PopupProps extends BaseContextMenu.Popup.Props {
  className?: string;
}
 
function Popup({ className, ...props }: PopupProps) {
  return (
    <BaseContextMenu.Popup
      className={clsx(styles.popup, className)}
      {...props}
    />
  );
}
 
interface ItemProps extends BaseContextMenu.Item.Props {
  className?: string;
}
 
function Item({ className, ...props }: ItemProps) {
  return (
    <BaseContextMenu.Item className={clsx(styles.item, className)} {...props} />
  );
}
 
interface SeparatorProps extends BaseContextMenu.Separator.Props {
  className?: string;
}
 
function Separator({ className, ...props }: SeparatorProps) {
  return (
    <BaseContextMenu.Separator
      className={clsx(styles.separator, className)}
      {...props}
    />
  );
}
 
interface GroupProps extends BaseContextMenu.Group.Props {
  className?: string;
}
 
function Group({ className, ...props }: GroupProps) {
  return (
    <BaseContextMenu.Group
      className={clsx(styles.group, className)}
      {...props}
    />
  );
}
 
interface GroupLabelProps extends BaseContextMenu.GroupLabel.Props {
  className?: string;
}
 
function GroupLabel({ className, ...props }: GroupLabelProps) {
  return (
    <BaseContextMenu.GroupLabel
      className={clsx(styles.groupLabel, className)}
      {...props}
    />
  );
}
 
interface RadioGroupProps extends BaseContextMenu.RadioGroup.Props {
  className?: string;
}
 
function RadioGroup({ className, ...props }: RadioGroupProps) {
  return (
    <BaseContextMenu.RadioGroup
      className={clsx(styles.radioGroup, className)}
      {...props}
    />
  );
}
 
interface RadioItemProps extends BaseContextMenu.RadioItem.Props {
  className?: string;
}
 
function RadioItem({ className, ...props }: RadioItemProps) {
  return (
    <BaseContextMenu.RadioItem
      className={clsx(styles.item, styles.radioItem, className)}
      {...props}
    />
  );
}
 
interface RadioItemIndicatorProps extends BaseContextMenu.RadioItemIndicator.Props {
  className?: string;
  children?: React.ReactNode;
}
 
function RadioItemIndicator({ className, children, ...props }: RadioItemIndicatorProps) {
  return (
    <BaseContextMenu.RadioItemIndicator
      className={clsx(styles.itemIndicator, className)}
      {...props}
    >
      {children || <Circle size={8} fill="currentColor" />}
    </BaseContextMenu.RadioItemIndicator>
  );
}
 
interface CheckboxItemProps extends BaseContextMenu.CheckboxItem.Props {
  className?: string;
}
 
function CheckboxItem({ className, ...props }: CheckboxItemProps) {
  return (
    <BaseContextMenu.CheckboxItem
      className={clsx(styles.item, styles.checkboxItem, className)}
      {...props}
    />
  );
}
 
interface CheckboxItemIndicatorProps extends BaseContextMenu.CheckboxItemIndicator.Props {
  className?: string;
  children?: React.ReactNode;
}
 
function CheckboxItemIndicator({ className, children, ...props }: CheckboxItemIndicatorProps) {
  return (
    <BaseContextMenu.CheckboxItemIndicator
      className={clsx(styles.itemIndicator, className)}
      {...props}
    >
      {children || <Check size={14} strokeWidth={2.5} />}
    </BaseContextMenu.CheckboxItemIndicator>
  );
}
 
const SubmenuRoot = BaseContextMenu.SubmenuRoot;
 
interface SubmenuTriggerProps extends BaseContextMenu.SubmenuTrigger.Props {
  className?: string;
}
 
function SubmenuTrigger({ className, ...props }: SubmenuTriggerProps) {
  return (
    <BaseContextMenu.SubmenuTrigger
      className={clsx(styles.item, styles.submenuTrigger, className)}
      {...props}
    />
  );
}
 
interface ArrowProps extends React.HTMLAttributes<HTMLDivElement> {
  className?: string;
  children?: React.ReactNode;
}
 
function Arrow({ className, children, ...props }: ArrowProps) {
  return (
    <div className={clsx(styles.arrow, className)} {...props}>
      {children || <ChevronRight size={16} />}
    </div>
  );
}
 
const ContextMenu = {
  Root,
  Trigger,
  Portal,
  Backdrop,
  Positioner,
  Popup,
  Item,
  Separator,
  Group,
  GroupLabel,
  RadioGroup,
  RadioItem,
  RadioItemIndicator,
  CheckboxItem,
  CheckboxItemIndicator,
  SubmenuRoot,
  SubmenuTrigger,
  Arrow,
};
 
export default ContextMenu;

Use Cases

Simple context menu

A basic context menu with items and a separator. Use the disabled prop to disable specific items.

Right click here

Use SubmenuRoot, SubmenuTrigger, and Arrow to create nested menus.

Right click here

With groups

Use Group and GroupLabel to organize items into labeled sections.

Right click here

With checkbox items

Use CheckboxItem and CheckboxItemIndicator for toggleable options within the menu.

Right click here

With radio items

Use RadioGroup, RadioItem, and RadioItemIndicator for single-selection options.

Right click here