# Context Menu

An accessible React context menu for Next.js: a right-click menu with a gliding highlight, shortcuts, checkbox and radio items and sub-menus, built on Radix.

Source: https://ui.fewtone.com/components/context-menu

## Installation

```bash
npx shadcn@latest add https://ui.fewtone.com/r/context-menu.json
```

## Usage

```tsx
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export function FileActions() {
  return (
    <ContextMenu>
      <ContextMenuTrigger className="rounded-lg border p-6" tabIndex={0}>
        Right-click here
      </ContextMenuTrigger>
      <ContextMenuContent className="w-48">
        <ContextMenuItem>
          Rename
          <ContextMenuShortcut>F2</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuItem>Duplicate</ContextMenuItem>
        <ContextMenuSeparator />
        <ContextMenuItem variant="destructive">Delete</ContextMenuItem>
      </ContextMenuContent>
    </ContextMenu>
  )
}
```

## API reference

### ContextMenu

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `onOpenChange` | `(open: boolean) => void` | – | Called when the menu opens or closes. |
| `modal` | `boolean` | `true` | While open, the rest of the page can't be scrolled or clicked, and screen readers only see the menu. |
| `dir` | `"ltr" \| "rtl"` | – | Reading direction. In rtl, sub-menus open to the left. |

### ContextMenuTrigger

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Use your own element, for example a card or a table row, instead of a span. |
| `disabled` | `boolean` | `false` | Leaves the browser's own menu. |

### ContextMenuContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `alignOffset` | `number` | `0` | Shifts it from the pointer, in px. |
| `loop` | `boolean` | `false` | Arrow keys wrap from the last item to the first, and back. |
| `collisionPadding` | `number` | `0` | Space to keep from the screen's edges, in px. |

### ContextMenuItem

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default" \| "destructive"` | `"default"` | Destructive is red, and the highlight takes a red tint as it lands on it. |
| `inset` | `boolean` | `false` | Indents the label to line up with items that start with an icon. |
| `disabled` | `boolean` | `false` | Skips the item. |
| `onSelect` | `(event: Event) => void` | – | Called when the item is picked with a click, Enter or Space. The menu then closes; call event.preventDefault() to keep it open. |
| `textValue` | `string` | – | The text typeahead matches, when the item's content isn't plain text. |

### ContextMenuCheckboxItem

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `checked` | `boolean \| "indeterminate"` | `false` | Whether it's on. Indeterminate draws a dash. |
| `onCheckedChange` | `(checked: boolean) => void` | – | Called with the new state when it's picked. |
| `disabled` | `boolean` | `false` | Skips the item. |
| `onSelect` | `(event: Event) => void` | – | Call event.preventDefault() to keep the menu open while switching. |

### ContextMenuRadioGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | – | The picked item's value. |
| `onValueChange` | `(value: string) => void` | – | Called with the new value when another item is picked. |

### ContextMenuRadioItem

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | – | The value this item picks. |
| `disabled` | `boolean` | `false` | Skips the item. |
| `onSelect` | `(event: Event) => void` | – | Call event.preventDefault() to keep the menu open while choosing. |

### ContextMenuLabel

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `inset` | `boolean` | `false` | Indents it to line up with items that start with an icon. |

### ContextMenuGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | – | The items, often after a ContextMenuLabel. |

### ContextMenuSeparator

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | – | Extra classes, merged with cn. |

### ContextMenuShortcut

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | – | The keys, for example ⌘R. It only shows them: bind the keys yourself. |

### ContextMenuSub

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | `boolean` | – | Whether the sub-menu is open, when you control it. |
| `defaultOpen` | `boolean` | `false` | Opens it at first. |
| `onOpenChange` | `(open: boolean) => void` | – | Called when it opens or closes. |

### ContextMenuSubTrigger

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `inset` | `boolean` | `false` | Indents the label to line up with items that start with an icon. |
| `disabled` | `boolean` | `false` | Skips the item. |

### ContextMenuSubContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `sideOffset` | `number` | `4` | Gap between the menu and the sub-menu, in px. |
| `alignOffset` | `number` | `-5` | Shifts it along the trigger, in px. The default lines its first item up with the trigger. |

### ContextMenuPortal

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `container` | `HTMLElement \| null` | `document.body` | Where to render instead. |

**When to use it:** Use ContextMenu for shortcuts to actions on a specific area or object when people right-click it: a file, a canvas, a table row. Offer the same actions somewhere visible too, such as a DropdownMenu or buttons, because touch and keyboard users may never find a right-click menu.
**Built on:** Radix Context Menu.
**Accessibility pattern:** https://www.w3.org/WAI/ARIA/apg/patterns/menubar/
**npm packages (installed by the CLI):** lucide-react@^1.52.0, radix-ui@^1.7.0.
**Installs these FewTone UI components with it:** `dropdown-menu`.

## Examples

### Default

Right-click the area, or long-press on touch. With the area focused, the keyboard's context-menu key or Shift+F10 opens it too. The settings and the text size keep this menu open, so you see the check draw in and the dot grow.

```tsx
"use client";

import {
  ContextMenu,
  ContextMenuCheckboxItem,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuLabel,
  ContextMenuRadioGroup,
  ContextMenuRadioItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuSub,
  ContextMenuSubContent,
  ContextMenuSubTrigger,
  ContextMenuTrigger,
} from "@/components/ui/context-menu";
import { useState } from "react";

const sizes = [
  { value: "small", label: "Small" },
  { value: "default", label: "Default" },
  { value: "large", label: "Large" },
];

/** A page's right-click menu. Settings and choices keep it open, so you see them change. */
export default function ContextMenuDefault() {
  const [bookmarks, setBookmarks] = useState(true);
  const [fullUrls, setFullUrls] = useState(false);
  const [size, setSize] = useState("default");
  const keepOpen = (event: Event) => event.preventDefault();

  return (
    <ContextMenu>
      {/* Focusable, so the keyboard's context-menu key or Shift+F10 opens it too. The border firms up while it's open. */}
      <ContextMenuTrigger
        tabIndex={0}
        className="flex h-36 w-full max-w-72 items-center justify-center rounded-lg border border-dashed border-input text-sm text-muted-foreground transition-colors duration-(--duration-fast) ease-out select-none data-[state=open]:border-ring data-[state=open]:text-foreground"
      >
        Right-click here
      </ContextMenuTrigger>
      <ContextMenuContent className="w-56">
        <ContextMenuItem>
          Back
          <ContextMenuShortcut>⌘[</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuItem disabled>
          Forward
          <ContextMenuShortcut>⌘]</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuItem>
          Reload
          <ContextMenuShortcut>⌘R</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuSub>
          <ContextMenuSubTrigger>More tools</ContextMenuSubTrigger>
          <ContextMenuSubContent className="w-48">
            <ContextMenuItem>Save page as…</ContextMenuItem>
            <ContextMenuItem>Create shortcut…</ContextMenuItem>
            <ContextMenuSeparator />
            <ContextMenuItem>Developer tools</ContextMenuItem>
          </ContextMenuSubContent>
        </ContextMenuSub>
        <ContextMenuSeparator />
        <ContextMenuCheckboxItem checked={bookmarks} onCheckedChange={setBookmarks} onSelect={keepOpen}>
          Show bookmarks bar
        </ContextMenuCheckboxItem>
        <ContextMenuCheckboxItem checked={fullUrls} onCheckedChange={setFullUrls} onSelect={keepOpen}>
          Show full URLs
        </ContextMenuCheckboxItem>
        <ContextMenuSeparator />
        <ContextMenuLabel>Text size</ContextMenuLabel>
        <ContextMenuRadioGroup value={size} onValueChange={setSize}>
          {sizes.map(({ value, label }) => (
            <ContextMenuRadioItem key={value} value={value} onSelect={keepOpen}>
              {label}
            </ContextMenuRadioItem>
          ))}
        </ContextMenuRadioGroup>
      </ContextMenuContent>
    </ContextMenu>
  );
}
```

## Related

- [Dropdown Menu](https://ui.fewtone.com/components/dropdown-menu.md): An accessible React dropdown menu for Next.js with a highlight that glides between items, shortcuts, checkbox and radio items and sub-menus, built on Radix.
- [Select](https://ui.fewtone.com/components/select.md): An accessible React select with a gliding highlight, groups, labels and a scrollable list, built on Radix Select.
