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.
Right-click is where an app puts the actions that would clutter a toolbar — rename, duplicate, move to folder, delete. The Context Menu component in Drivn covers that surface with React state and CSS, nothing else. No Radix, no portal, no floating-ui: the panel is a fixed div placed at the clientX / clientY the onContextMenu event reports, and two document listeners close it on outside mousedown or Escape.
Every snippet on this page imports from @/components/ui/context-menu — the exact path npx drivn add context-menu writes to — so they compile in a Next.js or Vite project with no edits. The component ships as typed .tsx source, so props like icon, shortcut, and destructive autocomplete at the call site. Once the file lands in your repo it is yours, which matters more here than for most components: menus are the thing every product ends up bending to its own shape.
Five patterns follow, ordered roughly by how often they show up in real apps: a basic menu with icons, shortcut hints on the right edge of each row, hover-opened submenus, destructive and disabled states, and labeled groups. Take the closest one and swap the items for yours. For the shadcn/ui side-by-side, 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.

