Component

Command Menu

IDE-style command palette with keyboard shortcuts, search filtering, grouped commands, and keyboard navigation hints.

  • Component
  • Tailwind
  • HTML
  • MIT

Preview

Live component

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

Install

Add to your project

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

Source

Implementation

code.html
<!-- Command Menu (IDE-style) -->
<div class="relative">
  <div
    id="command-trigger"
    class="flex items-center gap-3 w-full max-w-lg px-4 py-3 bg-gray-100 border border-gray-200 rounded-xl cursor-pointer hover:bg-gray-200 transition-colors"
    onclick="openCommandMenu()"
  >
    <svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
    </svg>
    <span class="flex-1 text-sm text-gray-500">Search commands, settings, or actions...</span>
    <kbd class="hidden sm:inline-flex items-center gap-1 px-2 py-1 text-xs text-gray-400 bg-white border border-gray-200 rounded">
      <span>⌘</span><span>K</span>
    </kbd>
  </div>

  <!-- Command Palette -->
  <div
    id="command-menu"
    class="hidden fixed inset-0 z-50 bg-black/50 flex items-start justify-center pt-24"
    onclick="closeCommandMenu(event)"
  >
    <div
      class="w-full max-w-xl bg-white rounded-2xl shadow-2xl border border-gray-200 overflow-hidden"
      onclick="event.stopPropagation()"
    >
      <!-- Search Input -->
      <div class="p-4 border-b border-gray-100">
        <div class="relative">
          <svg class="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
          </svg>
          <input
            type="text"
            id="command-search"
            class="w-full pl-12 pr-4 py-3 text-lg bg-gray-50 border-0 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
            placeholder="Type a command or search..."
            oninput="filterCommands(this.value)"
            autofocus
          />
        </div>
      </div>

      <!-- Command Categories -->
      <div class="max-h-96 overflow-y-auto">
        <!-- Navigation -->
        <div class="px-4 py-2">
          <p class="text-xs font-semibold text-gray-400 uppercase tracking-wider">Navigation</p>
        </div>
        <div class="px-2 pb-2">
          <button class="command-item w-full flex items-center gap-3 px-3 py-2.5 text-sm text-gray-700 rounded-lg hover:bg-blue-50 transition-colors" onclick="executeCommand('dashboard')">
            <svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
            </svg>
            <span>Go to Dashboard</span>
            <span class="ml-auto text-xs text-gray-400">⌘D</span>
          </button>
          <button class="command-item w-full flex items-center gap-3 px-3 py-2.5 text-sm text-gray-700 rounded-lg hover:bg-blue-50 transition-colors" onclick="executeCommand('projects')">
            <svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"></path>
            </svg>
            <span>Go to Projects</span>
            <span class="ml-auto text-xs text-gray-400">⌘P</span>
          </button>
          <button class="command-item w-full flex items-center gap-3 px-3 py-2.5 text-sm text-gray-700 rounded-lg hover:bg-blue-50 transition-colors" onclick="executeCommand('settings')">
            <svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
            </svg>
            <span>Open Settings</span>
            <span class="ml-auto text-xs text-gray-400">⌘,</span>
          </button>
        </div>

        <!-- Actions -->
        <div class="px-4 py-2 border-t border-gray-100">
          <p class="text-xs font-semibold text-gray-400 uppercase tracking-wider">Actions</p>
        </div>
        <div class="px-2 pb-2">
          <button class="command-item w-full flex items-center gap-3 px-3 py-2.5 text-sm text-gray-700 rounded-lg hover:bg-blue-50 transition-colors" onclick="executeCommand('new-file')">
            <svg class="w-5 h-5 text-gray-400" 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>Create New File</span>
            <span class="ml-auto text-xs text-gray-400">⌘N</span>
          </button>
          <button class="command-item w-full flex items-center gap-3 px-3 py-2.5 text-sm text-gray-700 rounded-lg hover:bg-blue-50 transition-colors" onclick="executeCommand('share')">
            <svg class="w-5 h-5 text-gray-400" 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 Project</span>
            <span class="ml-auto text-xs text-gray-400">⌘⇧S</span>
          </button>
          <button class="command-item w-full flex items-center gap-3 px-3 py-2.5 text-sm text-gray-700 rounded-lg hover:bg-blue-50 transition-colors" onclick="executeCommand('export')">
            <svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path>
            </svg>
            <span>Export as PNG</span>
            <span class="ml-auto text-xs text-gray-400">⌘E</span>
          </button>
        </div>

        <!-- Theme -->
        <div class="px-4 py-2 border-t border-gray-100">
          <p class="text-xs font-semibold text-gray-400 uppercase tracking-wider">Theme</p>
        </div>
        <div class="px-2 pb-4">
          <button class="command-item w-full flex items-center gap-3 px-3 py-2.5 text-sm text-gray-700 rounded-lg hover:bg-blue-50 transition-colors" onclick="executeCommand('theme')">
            <svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
            </svg>
            <span>Toggle Dark Mode</span>
            <span class="ml-auto text-xs text-gray-400">⌘⇧D</span>
          </button>
        </div>
      </div>

      <!-- Footer -->
      <div class="px-4 py-3 bg-gray-50 border-t border-gray-100 flex items-center justify-between text-xs text-gray-500">
        <div class="flex items-center gap-4">
          <span class="flex items-center gap-1"><kbd class="px-1.5 py-0.5 bg-white border border-gray-200 rounded">↑↓</kbd> Navigate</span>
          <span class="flex items-center gap-1"><kbd class="px-1.5 py-0.5 bg-white border border-gray-200 rounded">↵</kbd> Select</span>
        </div>
        <span class="flex items-center gap-1"><kbd class="px-1.5 py-0.5 bg-white border border-gray-200 rounded">Esc</kbd> Close</span>
      </div>
    </div>
  </div>
</div>

<script>
  function openCommandMenu() {
    document.getElementById('command-menu').classList.remove('hidden');
    document.getElementById('command-search').focus();
  }

  function closeCommandMenu(event) {
    if (event.target.id === 'command-menu') {
      document.getElementById('command-menu').classList.add('hidden');
    }
  }

  function filterCommands(query) {
    const normalizedQuery = query.toLowerCase();
    document.querySelectorAll('.command-item').forEach(item => {
      const text = item.textContent.toLowerCase();
      if (text.includes(normalizedQuery)) {
        item.classList.remove('hidden');
      } else {
        item.classList.add('hidden');
      }
    });
  }

  function executeCommand(command) {
    console.log('Executing command:', command);
    document.getElementById('command-menu').classList.add('hidden');
  }

  document.addEventListener('keydown', function(event) {
    if ((event.metaKey || event.ctrlKey) && event.key === 'k') {
      event.preventDefault();
      openCommandMenu();
    }
    if (event.key === 'Escape') {
      document.getElementById('command-menu').classList.add('hidden');
    }
  });
</script>
168 lines UTF-8 · LF · Spaces: 2
DevSnips AI ready Claude · Cursor · Windsurf

Agent skill & Model Context Protocol

Pass this component straight into your AI workflow. DevSnips exposes each resource as a structured, addressable registry entry, so your agent installs the real files instead of approximating them.

devsnips://components/dropdowns/command-menu
Prompt instruction
I want to use this DevSnips resource in production:

Page:
https://devsnips.site/library/tailwind/components/dropdowns/command-menu/

Install: npx devsnips add
Tailwind/Components/Dropdowns/command-menu

This is the Command Menu — IDE-style command palette with keyboard shortcuts, search filtering, grouped commands, and keyboard navigation hints.

Use this DevSnips resource as the starting point for my project.
Adapt it to my project's stack and conventions, fix any issues,
and make it production-ready while preserving its intended design
and behavior.

Paste into ChatGPT, Claude or your agent — it already carries the page URL and the exact install path.

Resource URI
devsnips://components/dropdowns/command-menu