CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-radix-ui--react-context-menu

React context menu primitive component with right-click and long-press support built on accessible menu foundations

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

Radix UI React Context Menu

A React context menu primitive component that provides accessible context menus with right-click and long-press support, built on top of Radix UI's menu foundation. Offers complete keyboard navigation, focus management, and customizable styling.

Package Information

  • Package Name: @radix-ui/react-context-menu
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @radix-ui/react-context-menu

Core Imports

import * as ContextMenu from "@radix-ui/react-context-menu";

Or import specific components:

import { 
  ContextMenu, 
  ContextMenuTrigger, 
  ContextMenuContent,
  ContextMenuItem 
} from "@radix-ui/react-context-menu";

For CommonJS:

const ContextMenu = require("@radix-ui/react-context-menu");

Basic Usage

import * as ContextMenu from "@radix-ui/react-context-menu";

function App() {
  return (
    <ContextMenu.Root>
      <ContextMenu.Trigger className="trigger">
        Right click me
      </ContextMenu.Trigger>
      <ContextMenu.Portal>
        <ContextMenu.Content className="context-menu">
          <ContextMenu.Item>
            Cut
          </ContextMenu.Item>
          <ContextMenu.Item>
            Copy
          </ContextMenu.Item>
          <ContextMenu.Item>
            Paste
          </ContextMenu.Item>
          <ContextMenu.Separator />
          <ContextMenu.Item>
            Delete
          </ContextMenu.Item>
        </ContextMenu.Content>
      </ContextMenu.Portal>
    </ContextMenu.Root>
  );
}

Architecture

The context menu system is built around several key architectural components:

Foundation Architecture:

  • Menu Primitive Foundation: Built on @radix-ui/react-menu, inheriting full accessibility and keyboard navigation
  • Primitive Component System: Components extend @radix-ui/react-primitive for composition with asChild prop support
  • Scoped Context System: Uses createContextScope for isolated context management and safe composition with other Radix components
  • Popper Positioning: Integrates @radix-ui/react-popper for collision detection and smart positioning

Interaction Model:

  • Context Management: React context manages open/closed state across component tree
  • Trigger System: Handles right-click (desktop) and long-press (touch) interactions with 700ms timer
  • Virtual Anchor: Uses virtual reference for positioning menu at mouse cursor location
  • Portal Rendering: Renders content outside DOM tree to avoid z-index and overflow issues
  • Focus Management: Automatic focus trapping, restoration, and roving tabindex for accessibility

Component Inheritance:

  • Most components extend their @radix-ui/react-menu counterparts and inherit all props
  • ContextMenuContent omits positioning props (side, sideOffset, align) that are automatically managed
  • All components support primitive composition via asChild prop where applicable

Capabilities

Root Component

The main context menu container that manages state and provides context to child components.

/**
 * Root context menu component
 */
interface ContextMenuProps {
  children?: React.ReactNode;
  onOpenChange?(open: boolean): void;
  dir?: Direction;
  modal?: boolean; // default: true
}

const ContextMenu: React.FC<ContextMenuProps>;

// Short name alias
const Root: typeof ContextMenu;

type Direction = "ltr" | "rtl";

Trigger Component

Element that triggers the context menu on right-click or long-press.

/**
 * Context menu trigger element
 */
interface ContextMenuTriggerProps extends React.ComponentPropsWithoutRef<typeof Primitive.span> {
  disabled?: boolean;
}

// Note: Primitive.span extends standard HTML span element with asChild support

const ContextMenuTrigger: React.ForwardRefExoticComponent<
  ContextMenuTriggerProps & React.RefAttributes<HTMLSpanElement>
>;

// Short name alias
const Trigger: typeof ContextMenuTrigger;

Portal Component

Portal component for rendering menu content outside the DOM tree.

/**
 * Portal for context menu content
 */
interface ContextMenuPortalProps {
  children?: React.ReactNode;
  container?: HTMLElement | null;
}

const ContextMenuPortal: React.FC<ContextMenuPortalProps>;

// Short name alias
const Portal: typeof ContextMenuPortal;

Content Component

Main container for menu items with positioning and focus management.

/**
 * Context menu content container
 * Extends MenuPrimitive.Content but omits positioning props that are automatically managed
 */
interface ContextMenuContentProps extends Omit<
  React.ComponentPropsWithoutRef<typeof MenuPrimitive.Content>,
  "onEntryFocus" | "side" | "sideOffset" | "align"
> {
  onEscapeKeyDown?(event: KeyboardEvent): void;
  onPointerDownOutside?(event: PointerDownOutsideEvent): void;
  onFocusOutside?(event: FocusOutsideEvent): void;
  onInteractOutside?(event: InteractOutsideEvent): void;
  forceMount?: true;
  loop?: boolean;
  onCloseAutoFocus?(event: Event): void;
  disableOutsidePointerEvents?: boolean;
  disableOutsideScroll?: boolean;
  trapFocus?: boolean;
  // Inherited positioning props (automatically managed)
  // side: 'right' (fixed)
  // sideOffset: 2 (fixed)
  // align: 'start' (fixed)
  // Collision detection
  avoidCollisions?: boolean;
  collisionBoundary?: Element | null | Array<Element | null>;
  collisionPadding?: number | Partial<Record<Side, number>>;
  // Advanced positioning
  arrowPadding?: number;
  sticky?: "partial" | "always";
  hideWhenDetached?: boolean;
  updatePositionStrategy?: "optimized" | "always";
}

const ContextMenuContent: React.ForwardRefExoticComponent<
  ContextMenuContentProps & React.RefAttributes<HTMLDivElement>
>;

// Short name alias
const Content: typeof ContextMenuContent;

Menu Structure Components

Components for organizing menu items and providing visual structure.

/**
 * Groups related menu items together
 */
interface ContextMenuGroupProps extends React.ComponentPropsWithoutRef<"div"> {}

const ContextMenuGroup: React.ForwardRefExoticComponent<
  ContextMenuGroupProps & React.RefAttributes<HTMLDivElement>
>;

/**
 * Accessible label for menu groups
 */
interface ContextMenuLabelProps extends React.ComponentPropsWithoutRef<"div"> {}

const ContextMenuLabel: React.ForwardRefExoticComponent<
  ContextMenuLabelProps & React.RefAttributes<HTMLDivElement>
>;

/**
 * Visual separator between menu items
 */
interface ContextMenuSeparatorProps extends React.ComponentPropsWithoutRef<"div"> {}

const ContextMenuSeparator: React.ForwardRefExoticComponent<
  ContextMenuSeparatorProps & React.RefAttributes<HTMLDivElement>
>;

/**
 * Arrow pointing from menu to trigger
 */
interface ContextMenuArrowProps extends React.ComponentPropsWithoutRef<"svg"> {
  width?: number;
  height?: number;
}

const ContextMenuArrow: React.ForwardRefExoticComponent<
  ContextMenuArrowProps & React.RefAttributes<SVGSVGElement>
>;

// Short name aliases
const Group: typeof ContextMenuGroup;
const Label: typeof ContextMenuLabel;
const Separator: typeof ContextMenuSeparator;
const Arrow: typeof ContextMenuArrow;

Menu Item Components

Interactive components for menu actions and selections.

/**
 * Basic interactive menu item
 */
interface ContextMenuItemProps extends React.ComponentPropsWithoutRef<"div"> {
  disabled?: boolean;
  onSelect?(event: Event): void;
  textValue?: string;
}

const ContextMenuItem: React.ForwardRefExoticComponent<
  ContextMenuItemProps & React.RefAttributes<HTMLDivElement>
>;

/**
 * Menu item with checkbox functionality
 */
interface ContextMenuCheckboxItemProps extends React.ComponentPropsWithoutRef<"div"> {
  checked?: boolean | "indeterminate";
  onCheckedChange?(checked: boolean): void;
  disabled?: boolean;
  onSelect?(event: Event): void;
  textValue?: string;
}

const ContextMenuCheckboxItem: React.ForwardRefExoticComponent<
  ContextMenuCheckboxItemProps & React.RefAttributes<HTMLDivElement>
>;

/**
 * Container for radio menu items
 */
interface ContextMenuRadioGroupProps extends React.ComponentPropsWithoutRef<"div"> {
  value?: string;
  onValueChange?(value: string): void;
}

const ContextMenuRadioGroup: React.ForwardRefExoticComponent<
  ContextMenuRadioGroupProps & React.RefAttributes<HTMLDivElement>
>;

/**
 * Menu item with radio button functionality
 */
interface ContextMenuRadioItemProps extends React.ComponentPropsWithoutRef<"div"> {
  value: string;
  disabled?: boolean;
  onSelect?(event: Event): void;
  textValue?: string;
}

const ContextMenuRadioItem: React.ForwardRefExoticComponent<
  ContextMenuRadioItemProps & React.RefAttributes<HTMLDivElement>
>;

/**
 * Visual indicator for checked/selected states
 */
interface ContextMenuItemIndicatorProps extends React.ComponentPropsWithoutRef<"span"> {
  forceMount?: true;
}

const ContextMenuItemIndicator: React.ForwardRefExoticComponent<
  ContextMenuItemIndicatorProps & React.RefAttributes<HTMLSpanElement>
>;

// Short name aliases
const Item: typeof ContextMenuItem;
const CheckboxItem: typeof ContextMenuCheckboxItem;
const RadioGroup: typeof ContextMenuRadioGroup;
const RadioItem: typeof ContextMenuRadioItem;
const ItemIndicator: typeof ContextMenuItemIndicator;

Submenu Components

Components for creating nested menu structures.

/**
 * Container for submenu functionality
 */
interface ContextMenuSubProps {
  children?: React.ReactNode;
  open?: boolean;
  defaultOpen?: boolean;
  onOpenChange?(open: boolean): void;
}

const ContextMenuSub: React.FC<ContextMenuSubProps>;

/**
 * Menu item that triggers a submenu
 */
interface ContextMenuSubTriggerProps extends React.ComponentPropsWithoutRef<"div"> {
  disabled?: boolean;
  textValue?: string;
}

const ContextMenuSubTrigger: React.ForwardRefExoticComponent<
  ContextMenuSubTriggerProps & React.RefAttributes<HTMLDivElement>
>;

/**
 * Container for submenu items
 */
interface ContextMenuSubContentProps extends React.ComponentPropsWithoutRef<"div"> {
  onEscapeKeyDown?(event: KeyboardEvent): void;
  onPointerDownOutside?(event: PointerDownOutsideEvent): void;
  onFocusOutside?(event: FocusOutsideEvent): void;
  onInteractOutside?(event: InteractOutsideEvent): void;
  forceMount?: true;
  loop?: boolean;
  sideOffset?: number;
  alignOffset?: number;
  avoidCollisions?: boolean;
  collisionBoundary?: Element | null | Array<Element | null>;
  collisionPadding?: number | Partial<Record<Side, number>>;
  arrowPadding?: number;
  sticky?: "partial" | "always";
  hideWhenDetached?: boolean;
}

const ContextMenuSubContent: React.ForwardRefExoticComponent<
  ContextMenuSubContentProps & React.RefAttributes<HTMLDivElement>
>;

// Short name aliases
const Sub: typeof ContextMenuSub;
const SubTrigger: typeof ContextMenuSubTrigger;
const SubContent: typeof ContextMenuSubContent;

Utility Functions

/**
 * Creates a scoped context for composing with other Radix components
 * Returns a tuple of context creation functions for advanced composition
 */
function createContextMenuScope(): [
  (scope: any) => any,
  (scope?: any) => any
];

Advanced Usage Examples

Complex Context Menu with All Features

import * as ContextMenu from "@radix-ui/react-context-menu";

function ComplexContextMenu() {
  const [checked, setChecked] = React.useState(false);
  const [selection, setSelection] = React.useState("option1");

  return (
    <ContextMenu.Root>
      <ContextMenu.Trigger className="trigger">
        Right click for full menu
      </ContextMenu.Trigger>
      
      <ContextMenu.Portal>
        <ContextMenu.Content className="context-menu">
          <ContextMenu.Label>Edit</ContextMenu.Label>
          <ContextMenu.Item onSelect={() => console.log("Cut")}>
            Cut
          </ContextMenu.Item>
          <ContextMenu.Item onSelect={() => console.log("Copy")}>
            Copy
          </ContextMenu.Item>
          <ContextMenu.Item onSelect={() => console.log("Paste")}>
            Paste
          </ContextMenu.Item>
          
          <ContextMenu.Separator />
          
          <ContextMenu.CheckboxItem 
            checked={checked} 
            onCheckedChange={setChecked}
          >
            <ContextMenu.ItemIndicator>✓</ContextMenu.ItemIndicator>
            Show hidden files
          </ContextMenu.CheckboxItem>
          
          <ContextMenu.Separator />
          
          <ContextMenu.Label>View</ContextMenu.Label>
          <ContextMenu.RadioGroup value={selection} onValueChange={setSelection}>
            <ContextMenu.RadioItem value="option1">
              <ContextMenu.ItemIndicator>•</ContextMenu.ItemIndicator>
              List view
            </ContextMenu.RadioItem>
            <ContextMenu.RadioItem value="option2">
              <ContextMenu.ItemIndicator>•</ContextMenu.ItemIndicator>
              Grid view
            </ContextMenu.RadioItem>
          </ContextMenu.RadioGroup>
          
          <ContextMenu.Separator />
          
          <ContextMenu.Sub>
            <ContextMenu.SubTrigger>More options</ContextMenu.SubTrigger>
            <ContextMenu.Portal>
              <ContextMenu.SubContent>
                <ContextMenu.Item>Export</ContextMenu.Item>
                <ContextMenu.Item>Import</ContextMenu.Item>
              </ContextMenu.SubContent>
            </ContextMenu.Portal>
          </ContextMenu.Sub>
          
          <ContextMenu.Arrow />
        </ContextMenu.Content>
      </ContextMenu.Portal>
    </ContextMenu.Root>
  );
}

Controlled Context Menu

import * as ContextMenu from "@radix-ui/react-context-menu";

function ControlledContextMenu() {
  const [open, setOpen] = React.useState(false);

  return (
    <ContextMenu.Root open={open} onOpenChange={setOpen}>
      <ContextMenu.Trigger>
        Controlled trigger (open: {String(open)})
      </ContextMenu.Trigger>
      <ContextMenu.Portal>
        <ContextMenu.Content>
          <ContextMenu.Item onSelect={() => setOpen(false)}>
            Close menu
          </ContextMenu.Item>
        </ContextMenu.Content>
      </ContextMenu.Portal>
    </ContextMenu.Root>
  );
}

Types and Utilities

// Event types for outside interaction detection
type PointerDownOutsideEvent = CustomEvent<{ originalEvent: PointerEvent }>;
type FocusOutsideEvent = CustomEvent<{ originalEvent: FocusEvent }>;
type InteractOutsideEvent = PointerDownOutsideEvent | FocusOutsideEvent;

// Positioning and layout types
type Side = "top" | "right" | "bottom" | "left";
type Align = "start" | "center" | "end";
type Direction = "ltr" | "rtl";

// Primitive component type (from @radix-ui/react-primitive)
type Primitive = {
  span: React.ForwardRefExoticComponent<React.ComponentPropsWithoutRef<"span"> & { asChild?: boolean }>;
  div: React.ForwardRefExoticComponent<React.ComponentPropsWithoutRef<"div"> & { asChild?: boolean }>;
  svg: React.ForwardRefExoticComponent<React.ComponentPropsWithoutRef<"svg"> & { asChild?: boolean }>;
};

// Menu primitive types (from @radix-ui/react-menu)
type MenuPrimitive = {
  Content: React.ForwardRefExoticComponent<MenuContentProps>;
  Item: React.ForwardRefExoticComponent<MenuItemProps>;
  CheckboxItem: React.ForwardRefExoticComponent<MenuCheckboxItemProps>;
  RadioGroup: React.ForwardRefExoticComponent<MenuRadioGroupProps>;
  RadioItem: React.ForwardRefExoticComponent<MenuRadioItemProps>;
  ItemIndicator: React.ForwardRefExoticComponent<MenuItemIndicatorProps>;
  Group: React.ForwardRefExoticComponent<MenuGroupProps>;
  Label: React.ForwardRefExoticComponent<MenuLabelProps>;
  Separator: React.ForwardRefExoticComponent<MenuSeparatorProps>;
  Arrow: React.ForwardRefExoticComponent<MenuArrowProps>;
  SubTrigger: React.ForwardRefExoticComponent<MenuSubTriggerProps>;
  SubContent: React.ForwardRefExoticComponent<MenuSubContentProps>;
  Portal: React.ForwardRefExoticComponent<MenuPortalProps>;
};

Styling

The components accept standard HTML attributes and can be styled with CSS. Radix UI provides CSS custom properties for advanced positioning and styling:

.context-menu {
  /* Custom properties available */
  transform-origin: var(--radix-context-menu-content-transform-origin);
  width: var(--radix-context-menu-content-available-width);
  height: var(--radix-context-menu-content-available-height);
}

Accessibility Features

  • Full keyboard navigation with arrow keys, Enter, and Escape
  • ARIA attributes for screen readers
  • Focus management and restoration
  • Support for both right-to-left (RTL) and left-to-right (LTR) text directions
  • Touch device support with long-press gestures
  • Proper focus trapping within open menus

Install with Tessl CLI

npx tessl i tessl/npm-radix-ui--react-context-menu
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@radix-ui/react-context-menu@2.2.x
Publish Source
CLI
Badge
tessl/npm-radix-ui--react-context-menu badge