Skip to main content

Context Menu

Context Menu is an overlay that shows actions for an item on right-click or long press, such as renaming or deleting a file.

Import

import { ContextMenu } from '@vortexlabs/vortex';
import type { ContextMenuProps } from '@vortexlabs/vortex';

Allied components

Other links

Right-click here
"use client";

import { ContextMenu, Text } from "@vortexlabs/vortex";
import { BoxArchive, Clone, PenLine, Trash } from "@vortexlabs/vortex-icons";

export const ContextMenuExample = () => {
  return (
    <ContextMenu width="280px">
      <ContextMenu.Trigger>
        {(attributes) => (
          <div
            {...attributes}
            tabIndex={0}
            style={{
              ...attributes.style,
              display: "grid",
              placeItems: "center",
              width: "100%",
              height: 240,
              border: "1px dashed var(--vortex-color-border-default)",
              borderRadius: "var(--vortex-border-radius-sm)",
            }}
          >
            <Text color="subtle">Right-click here</Text>
          </div>
        )}
      </ContextMenu.Trigger>
      <ContextMenu.Content>
        <ContextMenu.Section>
          <ContextMenu.Item startIcon={PenLine} shortcut="⌘R" onClick={() => {}}>
            Rename
          </ContextMenu.Item>
          <ContextMenu.Item startIcon={Clone} shortcut="⌘D" onClick={() => {}}>
            Duplicate
          </ContextMenu.Item>
        </ContextMenu.Section>
        <ContextMenu.Section>
          <ContextMenu.Item startIcon={BoxArchive} onClick={() => {}}>
            Archive
          </ContextMenu.Item>
          <ContextMenu.Item tone="critical" startIcon={Trash} shortcut="⌘⌫" onClick={() => {}}>
            Delete
          </ContextMenu.Item>
        </ContextMenu.Section>
      </ContextMenu.Content>
    </ContextMenu>
  );
};

Features

  • Opens at the pointer on right-click or touch long press.
  • Supports icons, shortcuts, sections, destructive items, and submenus.
  • Names the menu after its trigger by default.

Anatomy

<ContextMenu>
<ContextMenu.Trigger>
  {(attributes) => <div {...attributes} tabIndex={0}>File</div>}
</ContextMenu.Trigger>
<ContextMenu.Content>
  <ContextMenu.Item>Rename</ContextMenu.Item>
  <ContextMenu.Item>Delete</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu>
Component Part
Description
Holds the open state and positions the menu at the pointer.
Gives your element the attributes that open the menu on right-click, long press, or Shift+F10.
Holds the menu items and names the menu after the trigger.
Runs one action and closes the menu.
Groups related items.
Names a section.
Holds a nested menu that opens from its SubTrigger.
Opens the submenu on hover or with the arrow key.

Cards

Pass the attributes to a Card to open the menu from it. On a native element, spread them and merge their style with yours to keep the iOS long-press callout off.

Quarterly report.pdf
Edited 2 hours ago
// FileActions renders the ContextMenu.Content from the first example
<ContextMenu width="280px">
  <ContextMenu.Trigger>
    {(attributes) => (
      <Card attributes={{ ...attributes, tabIndex: 0 }}>
        <Text weight="medium">Quarterly report.pdf</Text>
        <Text size="body-sm-desktop" color="subtle">
          Edited 2 hours ago
        </Text>
      </Card>
    )}
  </ContextMenu.Trigger>
  <FileActions />
</ContextMenu>

Repeated Items

Give each row its own ContextMenu, so actions apply to the clicked item.

Brand guidelines.fig
Roadmap.pdf
Invoices.xlsx
// FileActions renders the ContextMenu.Content from the first example
const files = ["Brand guidelines.fig", "Roadmap.pdf", "Invoices.xlsx"];

<table style={{ width: "100%", borderCollapse: "collapse" }}>
  <tbody>
    {files.map((file) => (
      <ContextMenu key={file} width="280px">
        <ContextMenu.Trigger>
          {(attributes) => (
            <tr {...attributes} tabIndex={0}>
              <td style={{ padding: "var(--vortex-spacing-xs) var(--vortex-spacing-sm)" }}>
                <Text>{file}</Text>
              </td>
            </tr>
          )}
        </ContextMenu.Trigger>
        <FileActions />
      </ContextMenu>
    ))}
  </tbody>
</table>

Wrap a SubTrigger and a nested Content in SubMenu. It opens on hover or with the arrow key.

Right-click here
import { Link, Mail, PenLine, Share, Trash } from "@vortexlabs/vortex-icons";

<ContextMenu width="280px">
  <ContextMenu.Trigger>
    {(attributes) => <div {...attributes} tabIndex={0}>Right-click here</div>}
  </ContextMenu.Trigger>
  <ContextMenu.Content>
    <ContextMenu.Item startIcon={PenLine} shortcut="⌘R" onClick={() => {}}>
      Rename
    </ContextMenu.Item>
    <ContextMenu.SubMenu width="240px">
      <ContextMenu.SubTrigger startIcon={Share}>Share</ContextMenu.SubTrigger>
      <ContextMenu.Content>
        <ContextMenu.Item startIcon={Link} onClick={() => {}}>
          Copy link
        </ContextMenu.Item>
        <ContextMenu.Item startIcon={Mail} onClick={() => {}}>
          Email
        </ContextMenu.Item>
      </ContextMenu.Content>
    </ContextMenu.SubMenu>
    <ContextMenu.Item tone="critical" startIcon={Trash} shortcut="⌘⌫" onClick={() => {}}>
      Delete
    </ContextMenu.Item>
  </ContextMenu.Content>
</ContextMenu>

Right-click menus are hard to find, so also offer the actions in a visible DropdownMenu.

Accessibility

Key
Description
EnterSpace
Selects the focused item and closes the menu.
ShiftF10Menu key
Opens the menu below the focused area.
Down arrowUp arrow
Moves to the next or previous item, wrapping at the ends.
Right arrow
Opens a submenu (ArrowLeft in right-to-left layouts).
Left arrow
Closes a submenu (ArrowRight in right-to-left layouts).
Escape
Closes the menu.
Make the area focusable with tabIndex={0} and give it an accessible name.