React Context Menu Component Examples
Drop-in React Context Menu examples: right-click menu with icons and shortcuts, nested submenus, destructive items, grouped sections, and disabled state.
A context menu is the floating panel that opens on right-click — the affordance editors, file managers, and board apps use to expose secondary actions without crowding the surface. The Context Menu component in Drivn is built from React state and CSS only. There is no Radix dependency, no portal, no floating-ui — the menu is a fixed-positioned div placed at e.clientX / e.clientY from the onContextMenu event, and it closes on any mousedown outside or on Escape.
This page collects the patterns that ship in real applications. Each snippet imports from @/components/ui/context-menu, the same path the Drivn CLI writes the file to, so you can paste them into a Next.js or Vite project after npx drivn add context-menu and they compile without edits. The component lives in your repo after install — the runtime cost is zero new dependencies, just a few hundred lines of plain TSX you own end to end.
The examples below cover the common cases: a basic right-click menu with icons, items with keyboard shortcut hints, nested submenus via dot notation, destructive and disabled item states, and groups with labels. Pick the one closest to your use case and change the items to whatever your app needs to expose. To compare the API against the shadcn/ui equivalent, see Drivn vs shadcn Context Menu.
Items with keyboard shortcut hints
The shortcut prop on Item renders a right-aligned muted span — the source applies ml-auto text-xs text-muted-foreground so the hint sits opposite the label inside the same row. The text is purely visual; there is no key handling attached. Pair it with the actual hotkey logic in your app — a useEffect that listens for keydown and dispatches the same handler the menu item triggers. The shortcut string supports any glyph including the macOS Command symbol ⌘, Control ⌃, Shift ⇧, and Option ⌥. The example below renders ⌘X, ⌘C, and ⌘V next to Cut, Copy, and Paste — the canonical clipboard set most editor menus expose.
1 <ContextMenu> 2 <ContextMenu.Trigger> 3 <div>Right-click here</div> 4 </ContextMenu.Trigger> 5 <ContextMenu.Content> 6 <ContextMenu.Item icon={Scissors} shortcut="⌘X"> 7 Cut 8 </ContextMenu.Item> 9 <ContextMenu.Item icon={Copy} shortcut="⌘C"> 10 Copy 11 </ContextMenu.Item> 12 <ContextMenu.Item icon={Clipboard} shortcut="⌘V"> 13 Paste 14 </ContextMenu.Item> 15 </ContextMenu.Content> 16 </ContextMenu>
Destructive and disabled items
Two boolean props handle the most common item states. destructive swaps in red-tinted styles via text-destructive hover:bg-destructive/10 — the right look for irreversible actions like Delete or Remove. disabled applies opacity-50 pointer-events-none and forwards the disabled attribute to the underlying <button>, so the row neither reacts to hover nor fires onClick. Both flags compose with icon and shortcut so a destructive shortcut item like ⌘⌫ Delete reads the way users expect. The styles object lives at the top of the source file — see the Context Menu docs if you want to add a third state like "warning" by extending styles and threading a new prop through Item.
1 <ContextMenu> 2 <ContextMenu.Trigger> 3 <div>Right-click here</div> 4 </ContextMenu.Trigger> 5 <ContextMenu.Content> 6 <ContextMenu.Item icon={Star}> 7 Add to favorites 8 </ContextMenu.Item> 9 <ContextMenu.Item icon={Archive} disabled> 10 Archive 11 </ContextMenu.Item> 12 <ContextMenu.Separator /> 13 <ContextMenu.Item icon={Trash2} destructive shortcut="⌘⌫"> 14 Delete 15 </ContextMenu.Item> 16 </ContextMenu.Content> 17 </ContextMenu>
Grouped sections with labels
Group and Label partition the menu into themed sections. Label renders a small uppercase-feel header via px-3 py-1.5 text-xs font-medium text-muted-foreground — visually distinct from items but inside the same flow. Group wraps related items with py-1 so adjacent groups breathe without needing a hard Separator. Use this pattern when the menu carries enough actions that scanning by category becomes faster than reading top to bottom — for a notes app you might split History (Undo, Redo) from Format (Bold, Italic) from Actions (Share, Delete). After the basic installation, the same pattern works for both inline and submenu variants.
1 <ContextMenu> 2 <ContextMenu.Trigger> 3 <div>Right-click here</div> 4 </ContextMenu.Trigger> 5 <ContextMenu.Content> 6 <ContextMenu.Group> 7 <ContextMenu.Label>Actions</ContextMenu.Label> 8 <ContextMenu.Item icon={Undo2} shortcut="⌘Z"> 9 Undo 10 </ContextMenu.Item> 11 <ContextMenu.Item icon={Redo2} shortcut="⌘⇧Z"> 12 Redo 13 </ContextMenu.Item> 14 </ContextMenu.Group> 15 </ContextMenu.Content> 16 </ContextMenu>
Install Drivn in one command
Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.
npx drivn@latest createRequires Node 18+. Works with npm, pnpm, and yarn.
Frequently asked questions
Yes. The component imports React, the ChevronRight icon from lucide-react, and the local cn utility — nothing else. State lives in a small Context provider, the menu is a fixed-positioned div placed at the cursor coordinates from the onContextMenu event, and submenus open via the group-hover/sub CSS selector. There is no portal layer and no floating-ui, so the entire feature ships in roughly two hundred lines of TSX you own after install.
The ContextMenuRoot component already handles it. The internal onContextMenu callback calls e.preventDefault() before opening the Drivn menu, so the native browser menu never appears inside the trigger region. Anywhere outside the trigger, the browser default still works — that is the right behavior, since Drivn should not hijack right-click globally. Wrap only the regions where you want the custom menu to take over.
Not in the current implementation. Drivn's submenu opens via group-hover/sub CSS, which means the panel becomes visible while the cursor is over the parent Sub. Click-to-open submenus would require additional state per submenu and a click handler on SubTrigger. If your app needs that pattern, the Context Menu file is small enough to extend — store an open state on the Sub, toggle it from SubTrigger, and replace the group-hover/sub: classes with a conditional class on SubContent.
Yes. The Item component's onClick handler calls your onClick prop and then calls setOpen(false) from the Context Menu context. Both fire in sequence, so the action runs before the menu unmounts. If you need to keep the menu open after a click — for a multi-select pattern, for example — wrap the action in a custom item that does not close the menu, or render a checkbox row inside Item and stop propagation on the inner change handler.
Drivn does not include automatic edge-collision detection. The menu renders at { left: pos.x, top: pos.y } from the click coordinates, so right-clicking near the bottom-right corner of the page can clip the menu. For most app shells this is fine because triggers are inside content regions with margin to spare. If you need clipping protection, you can override the Content className with style overrides that read window.innerWidth and clamp the position, or wrap the whole component in a useEffect that reads getBoundingClientRect after open and adjusts.