Dropdown Menu
Displays a menu of actions or options triggered by a button
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">
<dui-icon name="circle-user-round" class="text-muted-foreground"/>
Ibn Battuta
</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-56">
<dui-dropdown-menu-label>My Account</dui-dropdown-menu-label>
<dui-dropdown-menu-separator/>
<dui-dropdown-menu-group>
<dui-dropdown-menu-item>
<dui-icon name="user"/>
Profile
<dui-dropdown-menu-shortcut>⇧⌘P</dui-dropdown-menu-shortcut>
</dui-dropdown-menu-item>
<dui-dropdown-menu-item>
<dui-icon name="luggage"/>
My Bookings
<dui-dropdown-menu-shortcut>⌘B</dui-dropdown-menu-shortcut>
</dui-dropdown-menu-item>
<dui-dropdown-menu-item>
<dui-icon name="settings"/>
Settings
<dui-dropdown-menu-shortcut>⌘,</dui-dropdown-menu-shortcut>
</dui-dropdown-menu-item>
</dui-dropdown-menu-group>
<dui-dropdown-menu-separator/>
<dui-dropdown-menu-item variant="DropdownMenuItemVariant.Destructive">
<dui-icon name="log-out"/>
Log out
<dui-dropdown-menu-shortcut>⇧⌘Q</dui-dropdown-menu-shortcut>
</dui-dropdown-menu-item>
</dui-dropdown-menu-content>
</dui-dropdown-menu>Usage
<dui-dropdown-menu> groups a trigger and its menu. The <dui-dropdown-menu-trigger> renders a <dui-button> (defaulting to ButtonVariant.Outline) that opens the menu using the HTML Popover API, and <dui-dropdown-menu-content> holds the menu items.
Compose the content from <dui-dropdown-menu-item> entries, grouping and labelling them with <dui-dropdown-menu-label>, <dui-dropdown-menu-separator> and <dui-dropdown-menu-group>. Add a trailing keyboard hint with <dui-dropdown-menu-shortcut>, and mark an item as dangerous with variant="DropdownMenuItemVariant.Destructive".
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">Open Menu</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-56">
<dui-dropdown-menu-label>My Account</dui-dropdown-menu-label>
<dui-dropdown-menu-separator/>
<dui-dropdown-menu-item>
<dui-icon name="user"/>
Profile
<dui-dropdown-menu-shortcut>⇧⌘P</dui-dropdown-menu-shortcut>
</dui-dropdown-menu-item>
<dui-dropdown-menu-item variant="DropdownMenuItemVariant.Destructive">
<dui-icon name="log-out"/>
Log out
</dui-dropdown-menu-item>
</dui-dropdown-menu-content>
</dui-dropdown-menu>Keyboard navigation, type-ahead, roving focus and close-on-select are handled for you — the menu is driven by the del-dropdown-menu web component, so no additional JavaScript is required.
Control where the menu opens relative to its trigger with the position
attribute on <dui-dropdown-menu-content>. It accepts the same PositionArea
values as the Popover Tag Helper and defaults to opening below the
trigger.
Examples
Checkbox Items
Use <dui-dropdown-menu-checkbox-item> for options that can be toggled independently. Set checked="true" to render an item as selected; the menu stays open as items are toggled.
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">
<dui-icon name="sliders-horizontal" class="text-muted-foreground"/>
Trip Filters
</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-56">
<dui-dropdown-menu-label>Show Categories</dui-dropdown-menu-label>
<dui-dropdown-menu-separator/>
<dui-dropdown-menu-checkbox-item checked="true">Flights</dui-dropdown-menu-checkbox-item>
<dui-dropdown-menu-checkbox-item checked="true">Accommodation</dui-dropdown-menu-checkbox-item>
<dui-dropdown-menu-checkbox-item>Car Rental</dui-dropdown-menu-checkbox-item>
</dui-dropdown-menu-content>
</dui-dropdown-menu>Radio Group
Wrap <dui-dropdown-menu-radio-item> elements in a <dui-dropdown-menu-radio-group> for a set of mutually exclusive options. The group's value attribute determines which item is selected.
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">
<dui-icon name="filter" class="text-muted-foreground"/>
Booking Status
</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-56">
<dui-dropdown-menu-label>Filter by Status</dui-dropdown-menu-label>
<dui-dropdown-menu-separator/>
<dui-dropdown-menu-radio-group value="confirmed">
<dui-dropdown-menu-radio-item value="confirmed">Confirmed</dui-dropdown-menu-radio-item>
<dui-dropdown-menu-radio-item value="pending">Pending</dui-dropdown-menu-radio-item>
<dui-dropdown-menu-radio-item value="cancelled">Cancelled</dui-dropdown-menu-radio-item>
</dui-dropdown-menu-radio-group>
</dui-dropdown-menu-content>
</dui-dropdown-menu>Submenus
Nest a <dui-dropdown-menu-sub> containing a <dui-dropdown-menu-sub-trigger> and <dui-dropdown-menu-sub-content> to create a submenu. Submenus open on hover or with the → key, and close with ←.
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">
<dui-icon name="ellipsis" class="text-muted-foreground"/>
Trip Actions
</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-56">
<dui-dropdown-menu-item>
<dui-icon name="ticket"/>
View Tickets
</dui-dropdown-menu-item>
<dui-dropdown-menu-item>
<dui-icon name="calendar-plus"/>
Add to Calendar
</dui-dropdown-menu-item>
<dui-dropdown-menu-separator/>
<dui-dropdown-menu-sub>
<dui-dropdown-menu-sub-trigger>
<dui-icon name="map-pin"/>
Add Destination
</dui-dropdown-menu-sub-trigger>
<dui-dropdown-menu-sub-content class="w-44">
<dui-dropdown-menu-item>Paris</dui-dropdown-menu-item>
<dui-dropdown-menu-item>Bangkok</dui-dropdown-menu-item>
<dui-dropdown-menu-item>Kyoto</dui-dropdown-menu-item>
<dui-dropdown-menu-item>Cape Town</dui-dropdown-menu-item>
</dui-dropdown-menu-sub-content>
</dui-dropdown-menu-sub>
<dui-dropdown-menu-separator/>
<dui-dropdown-menu-item variant="DropdownMenuItemVariant.Destructive">
<dui-icon name="trash-2"/>
Cancel Trip
</dui-dropdown-menu-item>
</dui-dropdown-menu-content>
</dui-dropdown-menu>Inset
Set inset="true" on an item, label or sub-trigger to indent it by the width of the leading icon/checkbox column. Use it so text-only items and labels line up with sibling items that have a leading icon or a checkbox/radio indicator.
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">
<dui-icon name="eye" class="text-muted-foreground"/>
Trip View
</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-56">
<!-- These items have a leading icon, so their label starts past the icon column -->
<dui-dropdown-menu-group>
<dui-dropdown-menu-item>
<dui-icon name="map"/>
Map
</dui-dropdown-menu-item>
<dui-dropdown-menu-item>
<dui-icon name="list"/>
List
</dui-dropdown-menu-item>
<dui-dropdown-menu-item>
<dui-icon name="calendar"/>
Calendar
</dui-dropdown-menu-item>
</dui-dropdown-menu-group>
<dui-dropdown-menu-separator/>
<!-- inset="true" indents the label and items so their text lines up with the icon'd items above -->
<dui-dropdown-menu-label inset="true">Sort by</dui-dropdown-menu-label>
<dui-dropdown-menu-group>
<dui-dropdown-menu-item inset="true">Price</dui-dropdown-menu-item>
<dui-dropdown-menu-item inset="true">Rating</dui-dropdown-menu-item>
<dui-dropdown-menu-item inset="true">Duration</dui-dropdown-menu-item>
</dui-dropdown-menu-group>
</dui-dropdown-menu-content>
</dui-dropdown-menu>Links
An item becomes a navigation link when you give it a URL — either a raw href or ASP.NET routing attributes (asp-page, asp-controller/asp-action, asp-route-*). When any of these is present the item renders as an <a role="menuitem">; otherwise it stays a <div role="menuitem">. Routing attributes are resolved by the framework, exactly like <dui-linkbutton>.
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">
<dui-icon name="compass" class="text-muted-foreground"/>
Quick Links
</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-56">
<dui-dropdown-menu-label>Navigate</dui-dropdown-menu-label>
<dui-dropdown-menu-separator/>
<dui-dropdown-menu-group>
<!-- Link to a Razor Page with a route value -->
<dui-dropdown-menu-item asp-page="/Booking/Details" asp-route-id="TRP-4821">
<dui-icon name="luggage"/>
View Booking
</dui-dropdown-menu-item>
<!-- Link to an MVC controller action -->
<dui-dropdown-menu-item asp-controller="Search" asp-action="Index">
<dui-icon name="search"/>
Search Trips
</dui-dropdown-menu-item>
<dui-dropdown-menu-item asp-controller="User" asp-action="Profile">
<dui-icon name="circle-user-round"/>
My Profile
</dui-dropdown-menu-item>
</dui-dropdown-menu-group>
<dui-dropdown-menu-separator/>
<!-- External link via a raw href -->
<dui-dropdown-menu-item href="https://help.voyager.travel" target="_blank" rel="noopener noreferrer">
<dui-icon name="life-buoy"/>
Help Center
<dui-icon name="external-link" class="ml-auto size-4"/>
</dui-dropdown-menu-item>
</dui-dropdown-menu-content>
</dui-dropdown-menu>Handling clicks
For non-link items, attach a click handler however you like — an inline onclick or addEventListener("click", …). Because the del-dropdown-menu web component dispatches a real click on the focused item for both mouse and keyboard activation (Enter / Space), your handler runs for either, and the menu closes on select afterwards.
<dui-stack gap="StackGap.Small" align="StackAlign.Center">
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">
<dui-icon name="ellipsis" class="text-muted-foreground"/>
Trip Actions
</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-56">
<dui-dropdown-menu-label>Trip #TRV-987</dui-dropdown-menu-label>
<dui-dropdown-menu-separator/>
<!-- A non-link item receives click events on any element: here via an inline onclick -->
<dui-dropdown-menu-item onclick="voyagerTripAction('Trip link copied to clipboard')">
<dui-icon name="link"/>
Copy Trip Link
</dui-dropdown-menu-item>
<!-- ...or wired up in script with addEventListener (see below) -->
<dui-dropdown-menu-item id="--duplicate-trip-item">
<dui-icon name="copy"/>
Duplicate Trip
</dui-dropdown-menu-item>
<dui-dropdown-menu-item id="--print-itinerary-item">
<dui-icon name="printer"/>
Print Itinerary
</dui-dropdown-menu-item>
</dui-dropdown-menu-content>
</dui-dropdown-menu>
<p class="text-muted-foreground text-sm" id="--trip-action-output">
Choose an action from the menu.
</p>
</dui-stack>
<script>
// The del-dropdown-menu web component dispatches a real click on the focused item for
// both mouse clicks and keyboard activation (Enter / Space), so a plain click listener
// works for either. The menu closes on select after your handler runs.
function voyagerTripAction(message) {
document.getElementById('--trip-action-output').textContent = message;
}
document
.getElementById('--duplicate-trip-item')
.addEventListener('click', () => voyagerTripAction('Trip duplicated'));
document
.getElementById('--print-itinerary-item')
.addEventListener('click', () => voyagerTripAction('Sending itinerary to the printer…'));
</script>Close on click
By default a plain <dui-dropdown-menu-item> closes the menu when activated, while checkbox and radio items stay open so you can adjust several at once. Override that per item with close-on-click: set close-on-click="false" on a plain item to keep the menu open (handy for a repeatable action), or close-on-click="true" on a checkbox/radio item to apply the choice and dismiss the menu. It applies to both mouse and keyboard activation.
<dui-stack gap="StackGap.Small" align="StackAlign.Center">
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">
<dui-icon name="settings-2" class="text-muted-foreground"/>
Booking Options
</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-64">
<dui-dropdown-menu-label>Availability</dui-dropdown-menu-label>
<dui-dropdown-menu-separator/>
<!-- A plain item closes the menu by default; close-on-click="false" keeps it
open so you can click it repeatedly. -->
<dui-dropdown-menu-item close-on-click="false" onclick="voyagerRefresh()">
<dui-icon name="refresh-cw"/>
Refresh availability
</dui-dropdown-menu-item>
<dui-dropdown-menu-separator/>
<dui-dropdown-menu-label>Preferences</dui-dropdown-menu-label>
<!-- Checkbox items stay open by default... -->
<dui-dropdown-menu-checkbox-item checked="true">Include breakfast</dui-dropdown-menu-checkbox-item>
<!-- ...while close-on-click="true" applies the choice and dismisses the menu. -->
<dui-dropdown-menu-checkbox-item close-on-click="true">Flexible dates (apply & close)</dui-dropdown-menu-checkbox-item>
</dui-dropdown-menu-content>
</dui-dropdown-menu>
<p class="text-muted-foreground text-sm" id="--booking-options-output">Refreshed 0 times.</p>
</dui-stack>
<script>
(() => {
let count = 0;
window.voyagerRefresh = () => {
count++;
document.getElementById('--booking-options-output').textContent =
'Refreshed ' + count + ' time' + (count === 1 ? '' : 's') + ' — the menu stayed open.';
};
})();
</script>Reacting to checkbox changes
Checkbox items report toggles through a bubbling checkedchange event, so you can listen on the item, its enclosing <dui-dropdown-menu-group>, or any ancestor — a single listener on the group is usually cleanest. event.detail.checked is the item's new state. Checkbox items carry no value, so identify which one changed via event.target. The event fires for both mouse and keyboard activation, and the menu deliberately stays open so multiple toggles feel natural.
<dui-stack gap="StackGap.Small" align="StackAlign.Center">
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">
<dui-icon name="sliders-horizontal" class="text-muted-foreground"/>
Trip Filters
</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-56">
<dui-dropdown-menu-label>Show Categories</dui-dropdown-menu-label>
<dui-dropdown-menu-separator/>
<!-- One "checkedchange" listener on the group catches every item (the event bubbles) -->
<dui-dropdown-menu-group id="--trip-filters">
<dui-dropdown-menu-checkbox-item checked="true">Flights</dui-dropdown-menu-checkbox-item>
<dui-dropdown-menu-checkbox-item checked="true">Accommodation</dui-dropdown-menu-checkbox-item>
<dui-dropdown-menu-checkbox-item>Car Rental</dui-dropdown-menu-checkbox-item>
</dui-dropdown-menu-group>
</dui-dropdown-menu-content>
</dui-dropdown-menu>
<p class="text-muted-foreground text-sm" id="--trip-filters-output"></p>
</dui-stack>
<script>
(() => {
const group = document.getElementById('--trip-filters');
const output = document.getElementById('--trip-filters-output');
// Checkbox items carry no value, so identify the toggled item via event.target.
// Seed the active set from whatever is checked on load.
const active = new Set(
[...group.querySelectorAll('[aria-checked="true"]')].map((el) => el.textContent.trim()),
);
const render = () => {
output.textContent = active.size ? 'Showing: ' + [...active].join(', ') : 'No categories selected';
};
// The menu stays open while toggling; the event fires for mouse and keyboard alike.
group.addEventListener('checkedchange', (event) => {
const label = event.target.textContent.trim();
event.detail.checked ? active.add(label) : active.delete(label);
render();
});
render();
})();
</script>Reacting to radio changes
Radio items report selection through a bubbling valuechange event. Listen on the <dui-dropdown-menu-radio-group> and read event.detail.value — the newly selected value — from a single handler (event.target is the selected item, if you need the element). The component enforces single-selection before the event fires, and the menu stays open.
<dui-stack gap="StackGap.Small" align="StackAlign.Center">
<dui-dropdown-menu>
<dui-dropdown-menu-trigger variant="ButtonVariant.Outline">
<dui-icon name="filter" class="text-muted-foreground"/>
Booking Status
</dui-dropdown-menu-trigger>
<dui-dropdown-menu-content class="w-56">
<dui-dropdown-menu-label>Filter by Status</dui-dropdown-menu-label>
<dui-dropdown-menu-separator/>
<!-- Listen once on the group; the event bubbles up from the selected radio item -->
<dui-dropdown-menu-radio-group id="--booking-status" value="confirmed">
<dui-dropdown-menu-radio-item value="confirmed">Confirmed</dui-dropdown-menu-radio-item>
<dui-dropdown-menu-radio-item value="pending">Pending</dui-dropdown-menu-radio-item>
<dui-dropdown-menu-radio-item value="cancelled">Cancelled</dui-dropdown-menu-radio-item>
</dui-dropdown-menu-radio-group>
</dui-dropdown-menu-content>
</dui-dropdown-menu>
<p class="text-muted-foreground text-sm" id="--booking-status-output"></p>
</dui-stack>
<script>
(() => {
const labels = { confirmed: 'Confirmed', pending: 'Pending', cancelled: 'Cancelled' };
const output = document.getElementById('--booking-status-output');
const show = (value) => {
output.textContent = 'Showing: ' + (labels[value] ?? value) + ' bookings';
};
// A single listener on the group gets the selected value from event.detail.value
// (event.target is the chosen item, if you need the element too). The menu stays open.
document.getElementById('--booking-status').addEventListener('valuechange', (event) => {
show(event.detail.value);
});
show('confirmed'); // reflect the group's initial value
})();
</script>API Reference
<dui-dropdown-menu>
The <dui-dropdown-menu> tag helper groups a trigger and its menu content. It
renders no element of its own — it only generates the shared id (honouring a
user-supplied id) that connects the trigger to the menu.
<dui-dropdown-menu-trigger>
The <dui-dropdown-menu-trigger> tag helper renders a <button> styled as a
<dui-button> that opens the menu via the native popovertarget attribute. In
addition to the attributes below, it forwards any standard <button>
attributes
to the rendered element.
Prop
Type
<dui-dropdown-menu-content>
The <dui-dropdown-menu-content> tag helper renders the menu itself as a
<del-dropdown-menu> web component with the popover
attribute,
anchored to the trigger. It forwards any standard global
attributes
to the rendered element.
Prop
Type
<dui-dropdown-menu-item>
The <dui-dropdown-menu-item> tag helper renders a <div role="menuitem">, or
an <a role="menuitem">
when an href or any routing attribute is supplied.
Prop
Type
Routing attributes
The item also accepts the same routing attributes as the ASP.NET Core Anchor
Tag
Helper
for generating the href. Supply either an href or these attributes — not
both.
Prop
Type
<dui-dropdown-menu-checkbox-item>
The <dui-dropdown-menu-checkbox-item> tag helper renders a <div role="menuitemcheckbox"> with a leading check indicator. Toggling it raises a
bubbling checkedchange event.
Prop
Type
<dui-dropdown-menu-radio-group>
The <dui-dropdown-menu-radio-group> tag helper renders a <div role="group">
that enforces single selection among its radio items and raises a bubbling
valuechange event when the selection changes.
Prop
Type
<dui-dropdown-menu-radio-item>
The <dui-dropdown-menu-radio-item> tag helper renders a <div role="menuitemradio"> with a leading check indicator, checked when its value
matches the group's value.
Prop
Type
<dui-dropdown-menu-label>
The <dui-dropdown-menu-label> tag helper renders a <div> used as a heading
for a group of items.
Prop
Type
<dui-dropdown-menu-separator>
The <dui-dropdown-menu-separator> tag helper renders a <div role="separator"> between groups of items. It has no attributes of its own.
<dui-dropdown-menu-shortcut>
The <dui-dropdown-menu-shortcut> tag helper renders a <span> for a trailing
keyboard hint. It has no attributes of its own.
<dui-dropdown-menu-group>
The <dui-dropdown-menu-group> tag helper renders a <div role="group"> for
grouping related items. It has no attributes of its own.
<dui-dropdown-menu-sub>
The <dui-dropdown-menu-sub> tag helper groups a sub-trigger and its submenu.
Like <dui-dropdown-menu>, it renders no element of its own and only generates
the shared id connecting the two.
<dui-dropdown-menu-sub-trigger>
The <dui-dropdown-menu-sub-trigger> tag helper renders a <div role="menuitem"> with a trailing chevron that opens its submenu on hover or
with the → key.
Prop
Type
<dui-dropdown-menu-sub-content>
The <dui-dropdown-menu-sub-content> tag helper renders a submenu as a
<del-dropdown-menu> popover anchored to its sub-trigger, accepting the same
position values as <dui-dropdown-menu-content>.
Prop
Type