Skip to content

Context Menu

Actions for a specific object, opened by right-click or long-press where the pointer is. A shortcut, never the only path.

SourceBase UIRegistry
Right-click here
import {
  ContextMenu,
  ContextMenuCheckboxItem,
  ContextMenuContent,
  ContextMenuGroup,
  ContextMenuItem,
  ContextMenuLabel,
  ContextMenuRadioGroup,
  ContextMenuRadioItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuSub,
  ContextMenuSubContent,
  ContextMenuSubTrigger,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export default function ContextMenuDemo() {
  return (
    <ContextMenu>
      <ContextMenuTrigger
        // Focusable, so Shift+F10 or the Menu key opens it from the keyboard.
        tabIndex={0}
        className="flex h-40 w-full max-w-sm items-center justify-center rounded-xl border border-dashed border-border-strong bg-card text-sm text-muted-foreground focus-ring"
      >
        Right-click here
      </ContextMenuTrigger>
      <ContextMenuContent className="w-60">
        <ContextMenuGroup>
          <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">
              <ContextMenuGroup>
                <ContextMenuItem>Save page as…</ContextMenuItem>
                <ContextMenuItem>Create shortcut…</ContextMenuItem>
                <ContextMenuItem>Developer tools</ContextMenuItem>
              </ContextMenuGroup>
            </ContextMenuSubContent>
          </ContextMenuSub>
        </ContextMenuGroup>
        <ContextMenuSeparator />
        <ContextMenuGroup>
          <ContextMenuCheckboxItem defaultChecked>
            Show bookmarks bar
          </ContextMenuCheckboxItem>
          <ContextMenuCheckboxItem>Show full URLs</ContextMenuCheckboxItem>
        </ContextMenuGroup>
        <ContextMenuSeparator />
        <ContextMenuGroup>
          <ContextMenuLabel inset>Profile</ContextMenuLabel>
          <ContextMenuRadioGroup defaultValue="work">
            <ContextMenuRadioItem value="personal">
              Personal
            </ContextMenuRadioItem>
            <ContextMenuRadioItem value="work">Work</ContextMenuRadioItem>
          </ContextMenuRadioGroup>
        </ContextMenuGroup>
      </ContextMenuContent>
    </ContextMenu>
  )
}

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/context-menu.json

Usage

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuGroup,
  ContextMenuItem,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"
<ContextMenu>
  <ContextMenuTrigger className="rounded-xl border border-dashed p-10">
    Right-click here
  </ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuGroup>
      <ContextMenuItem>Rename</ContextMenuItem>
      <ContextMenuItem>Duplicate</ContextMenuItem>
    </ContextMenuGroup>
  </ContextMenuContent>
</ContextMenu>

ContextMenuTrigger defines the area that responds to a right-click (or a long-press on touch). The menu opens at the pointer's position. Items, groups, submenus, checkbox and radio items work exactly like Dropdown Menu's.

Examples

Browser-style menu

Everything a context menu can hold: shortcuts, a disabled item, a submenu, checkbox items and a labelled radio group.

Right-click here
import {
  ContextMenu,
  ContextMenuCheckboxItem,
  ContextMenuContent,
  ContextMenuGroup,
  ContextMenuItem,
  ContextMenuLabel,
  ContextMenuRadioGroup,
  ContextMenuRadioItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuSub,
  ContextMenuSubContent,
  ContextMenuSubTrigger,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export default function ContextMenuDemo() {
  return (
    <ContextMenu>
      <ContextMenuTrigger
        // Focusable, so Shift+F10 or the Menu key opens it from the keyboard.
        tabIndex={0}
        className="flex h-40 w-full max-w-sm items-center justify-center rounded-xl border border-dashed border-border-strong bg-card text-sm text-muted-foreground focus-ring"
      >
        Right-click here
      </ContextMenuTrigger>
      <ContextMenuContent className="w-60">
        <ContextMenuGroup>
          <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">
              <ContextMenuGroup>
                <ContextMenuItem>Save page as…</ContextMenuItem>
                <ContextMenuItem>Create shortcut…</ContextMenuItem>
                <ContextMenuItem>Developer tools</ContextMenuItem>
              </ContextMenuGroup>
            </ContextMenuSubContent>
          </ContextMenuSub>
        </ContextMenuGroup>
        <ContextMenuSeparator />
        <ContextMenuGroup>
          <ContextMenuCheckboxItem defaultChecked>
            Show bookmarks bar
          </ContextMenuCheckboxItem>
          <ContextMenuCheckboxItem>Show full URLs</ContextMenuCheckboxItem>
        </ContextMenuGroup>
        <ContextMenuSeparator />
        <ContextMenuGroup>
          <ContextMenuLabel inset>Profile</ContextMenuLabel>
          <ContextMenuRadioGroup defaultValue="work">
            <ContextMenuRadioItem value="personal">
              Personal
            </ContextMenuRadioItem>
            <ContextMenuRadioItem value="work">Work</ContextMenuRadioItem>
          </ContextMenuRadioGroup>
        </ContextMenuGroup>
      </ContextMenuContent>
    </ContextMenu>
  )
}

Items in a list

Give every row its own ContextMenu, so the actions always apply to the object under the pointer. The trigger picks up data-popup-open while its menu is open — use it to keep the row highlighted, so people can see what the menu acts on.

Brand guidelines.pdf2.4 MB · Edited today
Q3 roadmap.fig18.1 MB · Edited yesterday
Launch checklist.md12 KB · Edited Mar 4
import {
  CopyIcon,
  DownloadIcon,
  FileTextIcon,
  FolderInputIcon,
  LinkIcon,
  PencilIcon,
  Share2Icon,
  StarIcon,
  Trash2Icon,
} from "lucide-react"

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuGroup,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuSub,
  ContextMenuSubContent,
  ContextMenuSubTrigger,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

const files = [
  { name: "Brand guidelines.pdf", meta: "2.4 MB · Edited today" },
  { name: "Q3 roadmap.fig", meta: "18.1 MB · Edited yesterday" },
  { name: "Launch checklist.md", meta: "12 KB · Edited Mar 4" },
]

export default function ContextMenuFileItem() {
  return (
    <div className="flex w-full max-w-md flex-col gap-1 rounded-xl border bg-card p-1.5 shadow-raised">
      {files.map((file) => (
        <ContextMenu key={file.name}>
          <ContextMenuTrigger
            tabIndex={0}
            className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm focus-ring transition-colors duration-fast hover:bg-accent data-popup-open:bg-accent"
          >
            <span className="flex size-9 shrink-0 items-center justify-center rounded-lg border bg-surface text-muted-foreground">
              <FileTextIcon className="size-4" />
            </span>
            <span className="flex min-w-0 flex-col">
              <span className="truncate font-medium text-foreground">
                {file.name}
              </span>
              <span className="text-body-xs text-muted-foreground">
                {file.meta}
              </span>
            </span>
          </ContextMenuTrigger>
          <ContextMenuContent className="w-56">
            <ContextMenuGroup>
              <ContextMenuItem>
                <PencilIcon />
                Rename
                <ContextMenuShortcut>↵</ContextMenuShortcut>
              </ContextMenuItem>
              <ContextMenuItem>
                <CopyIcon />
                Duplicate
                <ContextMenuShortcut>⌘D</ContextMenuShortcut>
              </ContextMenuItem>
              <ContextMenuItem>
                <StarIcon />
                Add to starred
              </ContextMenuItem>
              <ContextMenuItem>
                <FolderInputIcon />
                Move to…
              </ContextMenuItem>
            </ContextMenuGroup>
            <ContextMenuSeparator />
            <ContextMenuGroup>
              <ContextMenuSub>
                <ContextMenuSubTrigger>
                  <Share2Icon />
                  Share
                </ContextMenuSubTrigger>
                <ContextMenuSubContent className="w-44">
                  <ContextMenuGroup>
                    <ContextMenuItem>
                      <LinkIcon />
                      Copy link
                    </ContextMenuItem>
                    <ContextMenuItem>
                      <DownloadIcon />
                      Download
                    </ContextMenuItem>
                  </ContextMenuGroup>
                </ContextMenuSubContent>
              </ContextMenuSub>
            </ContextMenuGroup>
            <ContextMenuSeparator />
            <ContextMenuGroup>
              <ContextMenuItem variant="destructive">
                <Trash2Icon />
                Move to trash
                <ContextMenuShortcut>⌘⌫</ContextMenuShortcut>
              </ContextMenuItem>
            </ContextMenuGroup>
          </ContextMenuContent>
        </ContextMenu>
      ))}
    </div>
  )
}

Canvas objects

Design tools, editors and maps rely on context menus for object-level commands. Keep the vocabulary identical to the app's main menus and show the same shortcuts.

Frame 1
import {
  BringToFrontIcon,
  ClipboardPasteIcon,
  CopyIcon,
  GroupIcon,
  LockIcon,
  ScissorsIcon,
  SendToBackIcon,
} from "lucide-react"

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

export default function ContextMenuCanvas() {
  return (
    <ContextMenu>
      <ContextMenuTrigger
        // Focusable, so Shift+F10 or the Menu key opens it from the keyboard.
        tabIndex={0}
        className="relative flex h-52 w-full max-w-md items-center justify-center overflow-hidden rounded-xl border bg-surface focus-ring"
      >
        <div className="flex size-28 items-center justify-center rounded-lg border bg-card text-sm font-medium shadow-floating">
          Frame 1
        </div>
      </ContextMenuTrigger>
      <ContextMenuContent className="w-56">
        <ContextMenuGroup>
          <ContextMenuItem>
            <ScissorsIcon />
            Cut
            <ContextMenuShortcut>⌘X</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem>
            <CopyIcon />
            Copy
            <ContextMenuShortcut>⌘C</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem>
            <ClipboardPasteIcon />
            Paste here
            <ContextMenuShortcut>⌘V</ContextMenuShortcut>
          </ContextMenuItem>
        </ContextMenuGroup>
        <ContextMenuSeparator />
        <ContextMenuGroup>
          <ContextMenuItem>
            <BringToFrontIcon />
            Bring to front
            <ContextMenuShortcut>]</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem>
            <SendToBackIcon />
            Send to back
            <ContextMenuShortcut>[</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem>
            <GroupIcon />
            Group selection
            <ContextMenuShortcut>⌘G</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem>
            <LockIcon />
            Lock
            <ContextMenuShortcut>⇧⌘L</ContextMenuShortcut>
          </ContextMenuItem>
        </ContextMenuGroup>
      </ContextMenuContent>
    </ContextMenu>
  )
}

Guidelines

When to use

  • To speed up actions on objects people manipulate directly: files, rows, canvas layers, messages.
  • In dense, pointer-first tools where visible action buttons would crowd the interface.

When not to use

  • As the only way to reach an action. Right-click isn't discoverable, doesn't exist on most touch devices, and is awkward from a keyboard. Every item must also be available from a visible control — a Dropdown Menu on a ⋯ button, a toolbar, or the object's detail view.
  • On surfaces where people expect the browser's own menu, such as text they want to copy or links they want to open in a new tab.

Mirror the visible menus

A context menu is an accelerator for commands that already exist. Use the same labels, order and shortcuts as the row's ⋯ menu or the app's menubar, so people learn the vocabulary once. Put the most frequent actions first and destructive actions last, behind a separator.

Brand guidelines.pdf
DoThe same actions are one click away on a visible button.
Brand guidelines.pdf(right-click for options)
Don’tActions that only exist behind a right-click are invisible to most people.

Keep it short

Context menus are read at the pointer, under time pressure. Aim for fewer than ten items in two or three groups, avoid more than one level of submenus, and never put long-form content inside.

Accessibility

Context Menu is built on the Base UI Context Menu, which follows the WAI-ARIA menu pattern once open (the APG describes menus and menubars on one page).

KeyBehavior
ShiftF10
With focus inside the trigger area, opens the menu — the platform's keyboard equivalent of a right-click. Also the ContextMenu key on Windows keyboards.
↓↑
Moves focus between items; loops at the ends.
HomeEnd
Focuses the first or last item.
→←
Opens or closes a submenu.
EnterSpace
Activates the focused item.
A–Z
Typeahead to the next item starting with the typed characters.
Esc
Closes the menu.
  • Touch. A long-press on the trigger opens the menu, so the same actions work on phones and tablets.
  • Parity. Because context menus are hard to discover with assistive technology, the visible alternative isn't optional — it's what makes the actions accessible at all.

API reference

ContextMenu

The root. Accepts the Base UI ContextMenu.Root props.

PropTypeDefault
open

Controlled open state.

booleanNo default
defaultOpen

Initial open state when uncontrolled.

booleanfalse
onOpenChange

Called when the menu opens or closes.

(open: boolean, details) => voidNo default
loopFocus

Whether arrow-key focus wraps around.

booleantrue
disabled

Disables the menu; the browser's own menu shows instead.

booleanfalse

ContextMenuTrigger

The area that listens for right-click and long-press. Renders a <div> with select-none; style it like any container. It receives data-popup-open while its menu is open.

ContextMenuContent

PropTypeDefault
side

Preferred side relative to the pointer.

"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""right"
align

Alignment against the pointer position.

"start" | "center" | "end""start"
sideOffset

Gap from the pointer, in px.

number0
alignOffset

Shift along the alignment axis, in px.

number4

ContextMenuItem

PropTypeDefault
variant

Destructive items use the danger color.

"default" | "destructive""default"
inset

Adds leading space to align with items that have icons.

booleanfalse
onClick

Called when the item is activated.

(event) => voidNo default
closeOnClick

Whether activating the item closes the menu.

booleantrue
disabled

Dims the item and ignores clicks and Enter. Arrow keys still reach it, so a screen reader can announce it as unavailable.

booleanfalse
label

Text used for typeahead when the content isn't plain text.

stringNo default

Other parts

ContextMenuCheckboxItem, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut and ContextMenuGroup share their API with the equivalent Dropdown Menu parts. ContextMenuPortal is exported for custom compositions; ContextMenuContent already renders one.