Component

Context Menu

Right-click context menu with grouped actions, keyboard shortcuts, and screen-edge detection for file management applications.

  • Component
  • Tailwind
  • HTML
  • MIT

Preview

Live component

Interactive preview
Open preview
9:41 100%
devsnips.dev/library/tailwind/components/dropdowns/context-menu/
fluid · no fixed width 100%
No dependencies Production-ready

Install

Add to your project

terminal
npx devsnips add Tailwind/Components/Dropdowns/context-menu
registry path: Tailwind/Components/Dropdowns/context-menu

Source

Implementation

code.html
<!-- Context Menu -->
<div class="relative">
  <div
    id="context-trigger"
    class="w-full max-w-md mx-auto p-8 bg-white rounded-xl border-2 border-dashed border-gray-300 text-center cursor-context-menu"
    oncontextmenu="showContextMenu(event)"
  >
    <div class="flex flex-col items-center gap-3">
      <svg class="w-12 h-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
      </svg>
      <p class="text-gray-600">Right-click anywhere to open context menu</p>
      <p class="text-sm text-gray-400">This is the context trigger area</p>
    </div>
  </div>

  <!-- Context Menu -->
  <div
    id="context-menu"
    class="hidden fixed z-50 w-64 bg-white rounded-xl shadow-2xl border border-gray-200 overflow-hidden"
    style="left: 0; top: 0;"
  >
    <!-- File Actions -->
    <div class="px-3 py-2 border-b border-gray-100">
      <p class="text-xs font-semibold text-gray-400 uppercase tracking-wider">File</p>
    </div>
    <div class="py-1">
      <button class="w-full flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
        </svg>
        <span>New File</span>
        <kbd class="ml-auto px-1.5 py-0.5 text-xs text-gray-400 bg-gray-100 rounded">⌘N</kbd>
      </button>
      <button class="w-full flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"></path>
        </svg>
        <span>Open Folder</span>
        <kbd class="ml-auto px-1.5 py-0.5 text-xs text-gray-400 bg-gray-100 rounded">⌘O</kbd>
      </button>
      <button class="w-full flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"></path>
        </svg>
        <span>Share</span>
      </button>
    </div>

    <!-- Divider -->
    <div class="border-t border-gray-100"></div>

    <!-- Edit Actions -->
    <div class="px-3 py-2 border-b border-gray-100">
      <p class="text-xs font-semibold text-gray-400 uppercase tracking-wider">Edit</p>
    </div>
    <div class="py-1">
      <button class="w-full flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
        </svg>
        <span>Rename</span>
        <kbd class="ml-auto px-1.5 py-0.5 text-xs text-gray-400 bg-gray-100 rounded">F2</kbd>
      </button>
      <button class="w-full flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
        </svg>
        <span>Duplicate</span>
      </button>
      <button class="w-full flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"></path>
        </svg>
        <span>Copy Path</span>
        <kbd class="ml-auto px-1.5 py-0.5 text-xs text-gray-400 bg-gray-100 rounded">⌘⌥C</kbd>
      </button>
    </div>

    <!-- Divider -->
    <div class="border-t border-gray-100"></div>

    <!-- View -->
    <div class="py-1">
      <button class="w-full flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
        </svg>
        <span>Preview</span>
      </button>
    </div>

    <!-- Divider -->
    <div class="border-t border-gray-100"></div>

    <!-- Destructive -->
    <div class="py-1">
      <button class="w-full flex items-center gap-3 px-4 py-2 text-sm text-red-600 hover:bg-red-50 transition-colors">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
        </svg>
        <span>Delete</span>
        <kbd class="ml-auto px-1.5 py-0.5 text-xs text-gray-400 bg-gray-100 rounded">⌘⌫</kbd>
      </button>
    </div>
  </div>
</div>

<script>
  function showContextMenu(event) {
    event.preventDefault();
    const menu = document.getElementById('context-menu');
    menu.classList.remove('hidden');
    
    // Position the menu
    const x = event.clientX;
    const y = event.clientY;
    
    // Adjust if menu would go off screen
    const menuWidth = 256;
    const menuHeight = menu.offsetHeight;
    const adjustedX = x + menuWidth > window.innerWidth ? window.innerWidth - menuWidth - 10 : x;
    const adjustedY = y + menuHeight > window.innerHeight ? window.innerHeight - menuHeight - 10 : y;
    
    menu.style.left = adjustedX + 'px';
    menu.style.top = adjustedY + 'px';
  }

  document.addEventListener('click', function() {
    const menu = document.getElementById('context-menu');
    menu.classList.add('hidden');
  });

  document.addEventListener('keydown', function(event) {
    if (event.key === 'Escape') {
      const menu = document.getElementById('context-menu');
      menu.classList.add('hidden');
    }
  });
</script>
141 lines UTF-8 · LF · Spaces: 2

AI-ready

Available to your agent.

This component is reachable through the DevSnips CLI, MCP server and Skills integrations — one registry, one resource path.

Resource path

devsnips://components/dropdowns/context-menu

Continue browsing

View all