Skip to content

Dropdown Menu

Action and selection menus with runtime-owned open, highlight, and committed selection state.

---
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuRadioItem, DropdownMenuTrigger } from '@bejamas/registry/ui/dropdown-menu';
---
<DropdownMenu defaultValue="pro">
<DropdownMenuTrigger variant="outline">Open</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuLabel>Plans</DropdownMenuLabel>
<DropdownMenuGroup>
<DropdownMenuRadioItem value="starter">Starter</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="pro">Pro</DropdownMenuRadioItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
Terminal window
bunx bejamas add dropdown-menu
---
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioItem,
} from '@bejamas/ui/components/dropdown-menu';
---
<DropdownMenu>
<DropdownMenuTrigger variant="outline">Menu</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>Item 1</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
PropTypeDefault
defaultOpenboolean
defaultValuestring | null
defaultValuesstring[]
closeOnClickOutsideboolean
closeOnEscapeboolean
closeOnSelectboolean
lockScrollboolean
highlightItemOnHoverboolean
classstring""
---
import { Button } from '@bejamas/registry/ui/button';
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator } from '@bejamas/registry/ui/dropdown-menu';
---
<DropdownMenu>
<Button variant="outline" data-slot="dropdown-menu-trigger" class="cn-dropdown-menu-trigger">Account</Button>
<DropdownMenuContent>
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuGroup>
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Duplicate</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
---
import { Button } from '@bejamas/registry/ui/button';
import { DropdownMenu, DropdownMenuContent, DropdownMenuRadioItem } from '@bejamas/registry/ui/dropdown-menu';
---
<DropdownMenu defaultValue="pro">
<Button variant="outline" data-slot="dropdown-menu-trigger" class="cn-dropdown-menu-trigger">Plan</Button>
<DropdownMenuContent>
<DropdownMenuRadioItem value="starter">Starter</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="pro">Pro</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="team">Team</DropdownMenuRadioItem>
</DropdownMenuContent>
</DropdownMenu>
---
import { Button } from '@bejamas/registry/ui/button';
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent } from '@bejamas/registry/ui/dropdown-menu';
---
<DropdownMenu defaultValues={["email", "push"]} closeOnSelect={false}>
<Button variant="outline" data-slot="dropdown-menu-trigger" class="cn-dropdown-menu-trigger">Notifications</Button>
<DropdownMenuContent>
<DropdownMenuCheckboxItem value="email">Email</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem value="sms">SMS</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem value="push">Push</DropdownMenuCheckboxItem>
</DropdownMenuContent>
</DropdownMenu>

The dropdown menu emits custom events that you can listen to:

EventDetailDescription
dropdown-menu:open-change{ open, previousOpen, source, reason }Fired on real open-state changes
dropdown-menu:changesame detailDeprecated alias for dropdown-menu:open-change
dropdown-menu:highlight-change{ value, previousValue, item, previousItem, source }Fired when highlight changes
dropdown-menu:select{ value, item, itemType, source, checked? }Cancelable user activation event fired before commit
dropdown-menu:value-change{ value, previousValue, item, previousItem, source }Fired when radio selection commits
dropdown-menu:values-change{ values, previousValues, changedValue, checked, item, source }Fired when checkbox selection commits
<DropdownMenu id="my-dropdown" defaultValue="edit">
<DropdownMenuTrigger>Menu</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuRadioItem value="edit">Edit</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="delete">Delete</DropdownMenuRadioItem>
</DropdownMenuContent>
</DropdownMenu>
const dropdown = document.getElementById('my-dropdown');
dropdown.addEventListener('dropdown-menu:open-change', (e) => {
console.log('Is open:', e.detail.open);
});
dropdown.addEventListener('dropdown-menu:value-change', (e) => {
console.log('Selected:', e.detail.value);
});

You can control the dropdown menu programmatically by dispatching a dropdown-menu:set event:

const dropdown = document.getElementById('my-dropdown');
// Open the dropdown menu
dropdown.dispatchEvent(new CustomEvent('dropdown-menu:set', {
detail: { open: true }
}));
// Commit radio selection
dropdown.dispatchEvent(new CustomEvent('dropdown-menu:set', {
detail: { value: 'delete', source: 'restore' }
}));
// Commit checkbox selection
dropdown.dispatchEvent(new CustomEvent('dropdown-menu:set', {
detail: { values: ['email', 'push'], source: 'restore' }
}));

DropdownMenuPortal and DropdownMenuRadioGroup are available for low-level authored markup. Most consumers should keep using DropdownMenuContent directly.

The dropdown menu sets these data attributes that you can use for styling or querying state:

AttributeElementDescription
data-statedropdown-menu, dropdown-menu-contentCurrent state (open or closed)
data-valuedropdown-menuCurrent committed radio value
data-variantdropdown-menu-itemVisual variant (default or destructive)
data-sidedropdown-menu-contentComputed position (top, right, bottom, or left)
data-aligndropdown-menu-contentAlignment (start, center, or end)
data-highlightedmenu itemsPresent when item is focused
data-checkedradio and checkbox itemsPresent when item is committed as checked
data-disabledmenu itemsPresent when item is disabled