Context Menu

A composable, portal-based context menu for DayFlow UI. Supports nested submenus, labels, separators, and a built-in color picker. Automatically closes on outside click, scroll, resize, or Escape.

Installation

npm install @dayflow/ui-context-menu
pnpm add @dayflow/ui-context-menu
yarn add @dayflow/ui-context-menu
bun add @dayflow/ui-context-menu

For projects that already use Tailwind CSS, import the component-only bundle:

@import '@dayflow/ui-context-menu/dist/styles.components.css';
@import 'tailwindcss';

For projects that do not use Tailwind CSS, import the full stylesheet instead:

import {
  ContextMenu,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuLabel,
  ContextMenuSub,
  ContextMenuSubTrigger,
  ContextMenuSubContent,
  ContextMenuColorPicker,
} from '@dayflow/ui-context-menu';
import '@dayflow/ui-context-menu/dist/styles.css';

Basic Usage

Render ContextMenu at the cursor position captured from a contextmenu event.

import { useState } from 'react';
import {
  ContextMenu,
  ContextMenuItem,
  ContextMenuSeparator,
} from '@dayflow/ui-context-menu';

function MyComponent() {
  const [menu, setMenu] = useState<{ x: number; y: number } | null>(null);

  const handleContextMenu = (e: React.MouseEvent) => {
    e.preventDefault();
    setMenu({ x: e.clientX, y: e.clientY });
  };

  return (
    <div onContextMenu={handleContextMenu}>
      Right-click here
      {menu && (
        <ContextMenu x={menu.x} y={menu.y} onClose={() => setMenu(null)}>
          <ContextMenuItem onClick={() => console.log('Edit')}>
            Edit
          </ContextMenuItem>
          <ContextMenuItem onClick={() => console.log('Copy')}>
            Copy
          </ContextMenuItem>
          <ContextMenuSeparator />
          <ContextMenuItem onClick={() => console.log('Delete')} danger>
            Delete
          </ContextMenuItem>
        </ContextMenu>
      )}
    </div>
  );
}

With Labels and Icons

<ContextMenu x={x} y={y} onClose={onClose}>
  <ContextMenuLabel>Actions</ContextMenuLabel>
  <ContextMenuItem icon={<PencilIcon />} onClick={() => onEdit()}>
    Edit Event
  </ContextMenuItem>
  <ContextMenuItem icon={<CopyIcon />} onClick={() => onDuplicate()}>
    Duplicate
  </ContextMenuItem>
  <ContextMenuSeparator />
  <ContextMenuItem icon={<TrashIcon />} onClick={() => onDelete()} danger>
    Delete
  </ContextMenuItem>
</ContextMenu>

Nested Submenus

Wrap items with ContextMenuSub to create hover-triggered submenus. The submenu automatically repositions itself to avoid viewport overflow.

<ContextMenu x={x} y={y} onClose={onClose}>
  <ContextMenuItem onClick={() => onEdit()}>Edit</ContextMenuItem>
  <ContextMenuSub>
    <ContextMenuSubTrigger>Move to</ContextMenuSubTrigger>
    <ContextMenuSubContent>
      <ContextMenuItem onClick={() => onMove('work')}>Work</ContextMenuItem>
      <ContextMenuItem onClick={() => onMove('personal')}>
        Personal
      </ContextMenuItem>
    </ContextMenuSubContent>
  </ContextMenuSub>
  <ContextMenuSeparator />
  <ContextMenuItem danger onClick={() => onDelete()}>
    Delete
  </ContextMenuItem>
</ContextMenu>

Color Picker

ContextMenuColorPicker renders a row of preset swatches and an optional "Custom Color" action.

<ContextMenu x={x} y={y} onClose={onClose}>
  <ContextMenuLabel>Calendar Color</ContextMenuLabel>
  <ContextMenuColorPicker
    selectedColor={currentColor}
    onSelect={color => {
      updateCalendarColor(color);
      onClose();
    }}
    onCustomColor={() => openColorDialog()}
    customColorLabel='Custom color...'
  />
</ContextMenu>

Disabled Items

<ContextMenuItem onClick={() => onPaste()} disabled={!hasClipboard}>
  Paste
</ContextMenuItem>

API Reference

ContextMenu

PropTypeDescription
xnumberHorizontal position (e.g. event.clientX)
ynumberVertical position (e.g. event.clientY)
onClose() => voidCalled when the menu should close
childrenComponentChildrenMenu items
classNamestringExtra CSS classes for the menu container

ContextMenuItem

PropTypeDefaultDescription
onClick() => void—Click handler
childrenComponentChildren—Item label
iconComponentChildren—Icon rendered to the left of the label
dangerbooleanfalseRenders the item in destructive red
disabledbooleanfalseMakes the item non-interactive

ContextMenuColorPicker

PropTypeDefaultDescription
selectedColorstring—Currently active color (hex)
onSelect(color: string) => void—Called when a swatch is clicked
onCustomColor() => void—Called when the custom color row is clicked
customColorLabelstring"Custom Color"Label for the custom color action

Other exports

ComponentDescription
ContextMenuSeparatorHorizontal divider line
ContextMenuLabelNon-interactive section heading
ContextMenuSubWrapper that manages open state for a submenu
ContextMenuSubTriggerRow that reveals the submenu on hover
ContextMenuSubContentThe floating submenu panel

On this page