Skip to content

Documentation

neelam-ui

Search documentation

Getting Started
Forms
Overlays
Navigation
Data Display
Layout
AI & Chat
Blocks
GitHub repository

Context Menu

A menu opened by right-clicking a region, replacing the browser's own — the base implementation DropdownMenu is built on.

Right-click anywhere here

Usage#

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuTrigger,
} from "neelam-ui";
 
<ContextMenu>
  <ContextMenuTrigger>
    <div>Right-click anywhere here</div>
  </ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuItem>Back</ContextMenuItem>
    <ContextMenuSeparator />
    <ContextMenuItem>Reload</ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>

ContextMenuTrigger is a plain <div> around its children and suppresses the browser's own context menu in favour of this one.

Right-click is not discoverable

Nothing on screen advertises that a region has a context menu, and there is no right-click on touch at all. Every action here must also be reachable another way — a DropdownMenu button, a toolbar, a keyboard shortcut. Treat a context menu as an accelerator, never as the only path.

Positioning#

The menu opens at the cursor and is clamped to the viewport, so a right-click near an edge shifts the panel rather than letting it run off-screen. It is a native popover, so it renders in the top layer and escapes any overflow: hidden ancestor without a portal.

Opening is deferred a frame past the contextmenu event before showPopover() is called — the sequence this library uses for every popover.

Items#

ContextMenuItem closes the menu after activating by default; pass closeOnSelect={false} to keep it open. ContextMenuLabel is a non-interactive heading for a group, and ContextMenuShortcut is a muted, right-aligned hint placed as an item's last child.

Keyboard#

Keyboard shortcuts
KeyBehaviour
MenuShift+F10Opens the menu — but only while focus is on a focusable element inside the trigger region. See the note below.
Moves between items, wrapping at the ends. Disabled items are skipped.
HomeEndMoves to the first or last item.
EnterSpaceActivates the focused item.
EscapeCloses the menu.

The trigger is not focusable

ContextMenuTrigger renders a plain <div> with no tabIndex, and opens only from a contextmenu event. The platform's context-menu key fires that event at the focused element, so it reaches the trigger only by bubbling up from a focusable child. Wrap genuinely interactive content — a row of buttons, a link, a grid cell with tabIndex={0} — and keyboard users get the menu. Put a static <div> inside, as the demo above does, and there is no keyboard path to it at all. This is the concrete form of the discoverability warning above: always provide another route to every action.

Accessibility#

  • The panel is role="menu"; each item is a native <button> recategorized as role="menuitem", so activation stays native rather than being rebuilt on key handlers.
  • Roving focus moves real DOM focus between items — unlike Command, which keeps focus in its input and uses aria-activedescendant, because there the user must be able to keep typing.
  • ContextMenuSeparator is role="separator" with aria-orientation="horizontal".
  • The first item is focused on open, and the page behind is scroll-locked so the menu cannot drift from where it was opened.
  • Open and close transitions are dropped under prefers-reduced-motion.

API reference#

ContextMenu#

Props for ContextMenu
PropTypeDefault
onOpenChange((open: boolean) => void)
open

Controls the open state. Omit to let the menu manage its own state.

boolean

ContextMenuItem#

Props for ContextMenuItem
PropTypeDefault
closeOnSelect

Closes the menu after this item is activated. Defaults to `true`.

booleantrue
variant"default" | "destructive" | null