Dropdown Menu
Installation
npx @mielui/svelte add dropdown-menuyarn dlx @mielui/svelte add dropdown-menubunx @mielui/svelte add dropdown-menuUsage
Item runs callback when chosen, then closes the menu. Arrow keys, Home, End, and typing a label move between enabled items, and submenus open with the arrow key that points at them. An onclick handler on an item can call event.preventDefault() to keep the menu open.
The panel is one floating layer with a single hairline edge; the theme's chrome.borders setting does not change it.
<script lang="ts">
import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';
import Kbd from '@mielui/svelte/components/kbd';
function newFile() {
console.log('New file');
}
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>File</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item callback={newFile}>
New file
<Kbd shortcut="cmd+N" />
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>Examples
Item callbacks
Each item runs a callback, and the line under the menu reports the last choice.
Alex Morgan ยท alex@example.com
Basic menu
Newest first
Grouped items
Row actions
Release notes.md
Share menu
Sort menu
Configuration submenu
Nested choices for configuring a model, effort level, and response speed.
Dynamic width
Menus and submenus size to their longest item plus a buffer instead of hugging the trigger.
Glass surface
Set surface="glass" on DropdownMenu.Content for a translucent background with blur. Without a surface prop, the component follows --mielui-surface. An unset variable keeps it solid; surface="solid" overrides a global glass theme. The glass surface keeps an opaque fallback when backdrop filtering is unavailable and respects reduced-transparency preferences.
Alex Morgan ยท alex@example.com
Checkbox and radio items
CheckboxItem toggles an option and closes the menu, like any other item. Bind checked, or listen with onCheckedChange, which receives the new state.
RadioGroup holds one choice among its RadioItem children. Bind value on the group, or use onValueChange to react to the new value. Each RadioItem needs its own value. A checked CheckboxItem shows a check and the selected RadioItem shows a dot.
No changes yet
Controlling the menu
Bind open on Root to open or close the menu from your own code. onOpenChange runs when the person opens or closes it, and stays quiet for changes you make through open yourself. Trigger and SubTrigger also take onopen, which runs just before that trigger opens its panel. Use it to load items when someone looks.
Trigger opened it 0 times. Last change by a person: none yet.
Panel behavior
Content shares its panel with Popover, with menu defaults. A press outside closes the menu unless you set allowClickOutside={false}. dismissLayer={false} removes the invisible layer that catches the first outside press, so the page stays clickable while the menu is open.
focusTrap and lockScroll are both off for menus, so Tab leaves the menu and the page still scrolls. Turn either on for a menu that should hold attention. portal={false} renders the panel in place instead of at the end of the document. refElement anchors the panel to another element, and tabindex sets the panel's tab index.
Items and other parts
Item runs callback when chosen and then closes the menu. It is built on Button, so disabled skips it, and loading with labels shows progress on a row that starts slow work. variant="destructive" colors the row for an action that removes something and tints the highlight while it is active. Item, CheckboxItem, RadioItem, Trigger and SubTrigger accept element to bind the DOM node, and onkeydown on Item runs before the menu handles the key. Rows always use the menu row style, so Button's other variants, size and unstyled do not apply to them. A row is always a button and does not take href. Navigate from callback.
Root, Content, Label, Separator, Sub, SubContent and RadioGroup render their children and accept class and style like any element.
API reference
DropdownMenu.Root
Provides dropdown menu state.
openBindablebooleanWhether it is open. Bind it to control the state from outside.
Default
falseonOpenChange((open: boolean) => void)Called with the new state when the person opens or closes it, not when you set
open.childrenSnippet<[]>Content rendered inside.
DropdownMenu.CheckboxItem
checkedBindablebooleanWhether the item is checked. Bindable.
Default
falseonCheckedChange((checked: boolean) => void)Called with the new state when it changes.
childrenSnippet<[]>Content rendered inside.
elementBindableHTMLButtonElement | HTMLAnchorElementBindable reference to the DOM element.
class(string & (ClassValue | null))Classes added to the element.
DropdownMenu.Content
Renders the menu surface.
childrenRequiredSnippet<[]>Content rendered inside.
classstringClasses added to the element.
dynamicbooleanLets the panel grow to fit its content instead of matching the trigger width.
Default
falsetabindexnumberTab index of the panel.
Default
-1role"dialog" | "alertdialog" | "menu" | "listbox" | "none"ARIA role of the panel.
Default
'menu'surface"solid" | "glass"Surface treatment. Omit it to inherit
--mielui-surfacefrom the theme.allowClickOutsidebooleanCloses when the pointer is pressed outside it. Set false to keep it open until it is dismissed another way.
Default
truedismissLayerbooleanRender the full-viewport dismiss layer under the panel while open. Defaults to
true. Set tofalsefor triggers that must stay clickable while open (e.g. an input-style combobox trigger); outside pointer dismissal still applies viaallowClickOutside.Default
trueportalbooleanMoves the panel to the end of the document so ancestors cannot clip it. Set false to render it in place.
Default
truerefElementVirtualElementPositions the panel against this element or virtual element instead of the trigger. The panel then opens to the right of it, as a submenu does.
focusTrapbooleanTrap Tab focus inside the panel while open. Defaults to
true.Default
falselockScrollbooleanLock document scrolling while the panel is open. Defaults to
true.Default
false
DropdownMenu.Item
Renders a menu action.
callback(() => void)Called when the item is chosen.
variant"destructive"Marks an action that deletes or cannot be undone. Omit it for a regular item.
childrenSnippet<[]>Content rendered inside.
onkeydown((event: KeyboardEvent) => void)Called on key down, before the button handles the key itself.
onclick((event: MouseEvent) => void)Called when the button is activated. Loading and disabled buttons do not call it.
statusButtonStatusControlled visual state. Loading remains focusable and refuses activation.
disabledbooleanPrevents activation. A disabled link loses its destination and is skipped by Tab.
Default
falseelementBindableHTMLButtonElement | HTMLAnchorElementBindable reference to the rendered DOM element. Type is the union of the two possible element types -- narrow at the use site:
loadingbooleanConvenience alias for
status="loading".labelsButtonLabelsOverrides the built-in text and accessible names. Every key is optional and English is the fallback.
DropdownMenu.Label
Labels a menu section.
childrenRequiredSnippet<[]>Content rendered inside.
classstringClasses added to the element.
DropdownMenu.RadioGroup
valueBindablestringValue of the selected item. Bindable.
Default
''onValueChange((value: string) => void)Called with the new value when it changes.
childrenSnippet<[]>Content rendered inside.
DropdownMenu.RadioItem
valueRequiredstring | (string & string[])Value this item selects.
childrenSnippet<[]>Content rendered inside.
elementBindableHTMLButtonElement | HTMLAnchorElementBindable reference to the DOM element.
class(string & (ClassValue | null))Classes added to the element.
DropdownMenu.Separator
Separates menu items.
childrenSnippet<[]>Content rendered inside.
classstringClasses added to the element.
DropdownMenu.Sub
Provides submenu state.
childrenSnippet<[]>Content rendered inside.
DropdownMenu.SubContent
Renders a submenu surface.
surface"solid" | "glass"Surface treatment. Omit it to inherit
--mielui-surfacefrom the theme.childrenRequiredSnippet<[]>Content rendered inside.
classstringClasses added to the element.
dynamicbooleanLets the panel grow to fit its content instead of matching the trigger width.
Default
false
DropdownMenu.SubTrigger
Opens a submenu.
classstringAdditional classes for the element.
childrenSnippet<[]>Content rendered inside.
elementBindableHTMLButtonElement | HTMLAnchorElementBindable reference to the DOM element.
onclick((event: MouseEvent) => void)Called when it is clicked or activated.
onopen(() => void)Called immediately before this trigger opens its submenu.
stylestringInline styles for the element.
DropdownMenu.Trigger
Opens the dropdown menu.
childrenRequiredSnippet<[]>Content rendered inside.
classstringClasses added to the element.
variantButtonVariantButton style of the trigger.
stylestringInline styles for the element.
onclick((event: MouseEvent) => void)Called when it is clicked or activated.
size"sm" | "md" | "lg" | "icon"Control height of the trigger.
elementBindableHTMLButtonElement | HTMLAnchorElementBindable reference to the DOM element.
unstyledbooleanForwarded to Button: render with
classalone, skipping variant/size.iconbooleanShows the chevron after the label.
Default
falseonopen(() => void)Called immediately before this trigger opens its popover.
pnpm dlx @mielui/svelte add dropdown-menu