Unstyled, accessible Dropdown and Command Palette components distributed via the shadcn registry.
Zero external runtime dependencies — source is copied directly into your project so you style and own it completely.
Requires shadcn CLI (npx shadcn@latest init to set up if needed).
# Install both Dropdown and Command Palette
npx shadcn add https://muleyuck.github.io/cmdrop/registry.json
# Or install individually
npx shadcn add https://muleyuck.github.io/cmdrop/registry.json/dropdown
npx shadcn add https://muleyuck.github.io/cmdrop/registry.json/commandFiles are copied to components/ui/dropdown/ and components/ui/command/.
import { Content, Dropdown, Item, Trigger } from "@/components/ui/dropdown"
const [value, setValue] = useState<string>()
<Dropdown value={value} onValueChange={setValue}>
<Trigger>Open</Trigger>
<Content>
<Item value="react">React</Item>
<Item value="vue">Vue</Item>
<Item value="svelte">Svelte</Item>
</Content>
</Dropdown>Supports multiple selection, groups, separators, and filterable search with highlighted matches. See all examples →
| Component | Key props | Data attributes |
|---|---|---|
Dropdown |
value, onValueChange, multiple, open, onOpenChange, filterable |
— |
Trigger |
HTML <button> attributes |
data-state="open|closed" |
Content |
HTML <div> attributes |
data-side="top|bottom" |
Item |
value, disabled, onSelect |
data-highlighted, data-selected, data-disabled |
Group |
label, labelClassName |
— |
Input |
HTML <input> attributes |
— |
Empty |
HTML <div> attributes |
— |
Separator |
HTML <hr> attributes |
— |
useHighlight |
{ className } |
Returns (text: string) => ReactNode |
Keyboard: ↑/↓ to navigate, Enter to select, Escape to close. Pressing ↑/↓ on the Trigger opens the list and jumps to the last/first item.
Command listens globally for ⌘K / Ctrl+K to toggle open/closed.
import { Command, Dialog, Empty, Group, Input, Item, List, Overlay, useHighlight } from "@/components/ui/command"
const [open, setOpen] = useState(false)
<Command open={open} onOpenChange={setOpen}>
<Overlay className="fixed inset-0 bg-black/50 backdrop-blur-sm" />
<Dialog className="fixed left-1/2 top-[15vh] -translate-x-1/2 w-full max-w-lg rounded-xl bg-white shadow-2xl">
<Input placeholder="Search commands…" />
<List className="max-h-72 overflow-y-auto">
<Item value="Home" onSelect={() => { window.scrollTo({ top: 0 }); setOpen(false) }}>Home</Item>
</List>
<Empty>No results found.</Empty>
</Dialog>
</Command>| Component | Key props | Data attributes |
|---|---|---|
Command |
open, onOpenChange |
— |
Dialog |
autoFocus (default true), HTML <div> attributes |
— |
Overlay |
HTML <div> attributes |
— |
Input |
HTML <input> attributes |
— |
List |
HTML <div> attributes |
— |
Item |
value, onSelect (required), disabled |
data-highlighted, data-disabled |
Group |
label, labelClassName, forceMount |
— |
Empty |
HTML <div> attributes |
— |
Separator |
HTML <hr> attributes |
— |
useHighlight |
{ className } |
Returns (text: string) => ReactNode |
Item.value defaults to the text content of children when omitted.
Keyboard: ↑/↓ to navigate, Enter to select, Escape to close, ⌘K/Ctrl+K to toggle.