Dropdown Menu
Displays a menu to the user — such as a set of actions or functions — triggered by a button.
This component uses newly available browser features
This component uses CSS Anchor Positioning which has only been widely supported since January 2026. If you're targeting older or less mainstream browsers then maybe this component isn't for you, yet.
Alternatively you can use Singlestage 0.4 which positions the component correctly but with limited flexibility for alignment, or roll your own using Singlestage CSS classes for consistent styling.
More Infouse leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownMenuExample() -> impl IntoView { view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">"Open"</Button> </DropdownMenuTrigger> <DropdownMenuContent class="w-40" align="start"> <DropdownMenuGroup> <DropdownMenuLabel>"My Account"</DropdownMenuLabel> <DropdownMenuItem> "Profile" <DropdownMenuShortcut>"⇧⌘P"</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem> "Billing" <DropdownMenuShortcut>"⌘B"</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem> "Settings" <DropdownMenuShortcut>"⌘S"</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem>"Team"</DropdownMenuItem> <DropdownMenuSub> <DropdownMenuSubTrigger>"Invite users"</DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuItem>"Email"</DropdownMenuItem> <DropdownMenuItem>"Message"</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem>"More..."</DropdownMenuItem> </DropdownMenuSubContent> </DropdownMenuSub> <DropdownMenuItem> "New Team" <DropdownMenuShortcut>"⌘+T"</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem>"GitHub"</DropdownMenuItem> <DropdownMenuItem>"Support"</DropdownMenuItem> <DropdownMenuItem disabled=true>"API"</DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem> "Log out" <DropdownMenuShortcut>"⇧⌘Q"</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> } }
Basic
A basic dropdown menu with labels and separators.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownBasicExample() -> impl IntoView { view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">"Open"</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuGroup> <DropdownMenuLabel>"My Account"</DropdownMenuLabel> <DropdownMenuItem>"Profile"</DropdownMenuItem> <DropdownMenuItem>"Billing"</DropdownMenuItem> <DropdownMenuItem>"Settings"</DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuItem>"GitHub"</DropdownMenuItem> <DropdownMenuItem>"Support"</DropdownMenuItem> <DropdownMenuItem disabled=true>"API"</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> } }
Submenu
Use MenuSub to nest secondary actions.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownSubExample() -> impl IntoView { view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">"Open"</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuGroup> <DropdownMenuItem>"Team"</DropdownMenuItem> <DropdownMenuSub> <DropdownMenuSubTrigger>"Invite users"</DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuItem>"Email"</DropdownMenuItem> <DropdownMenuItem>"Message"</DropdownMenuItem> <DropdownMenuSub> <DropdownMenuSubTrigger>"More options"</DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuItem>"Calendly"</DropdownMenuItem> <DropdownMenuItem>"Slack"</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem>"Webhook"</DropdownMenuItem> </DropdownMenuSubContent> </DropdownMenuSub> <DropdownMenuSeparator /> <DropdownMenuItem>"Advanced..."</DropdownMenuItem> </DropdownMenuSubContent> </DropdownMenuSub> <DropdownMenuItem> "New Team" <DropdownMenuShortcut>"⌘+T"</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> } }
Shortcuts
Add MenuShortcut to show keyboard hints.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownShortcutsExample() -> impl IntoView { view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">"Open"</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuGroup> <DropdownMenuLabel>"My Account"</DropdownMenuLabel> <DropdownMenuItem> "Profile" <DropdownMenuShortcut>"⇧⌘P"</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem> "Billing" <DropdownMenuShortcut>"⌘B"</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem> "Settings" <DropdownMenuShortcut>"⌘S"</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuItem> "Log out" <DropdownMenuShortcut>"⇧⌘Q"</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> } }
Icons
Combine icons with labels for quick scanning.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownIconsExample() -> impl IntoView { view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">"Open"</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuItem>{icon!(icondata::LuUser)} "Profile"</DropdownMenuItem> <DropdownMenuItem>{icon!(icondata::LuCreditCard)} "Billing"</DropdownMenuItem> <DropdownMenuItem>{icon!(icondata::LuSettings)} "Settings"</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem variant="destructive"> {icon!(icondata::LuLogOut)} "Log out" </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> } }
Checkboxes
Dropdown menus can use Checkboxes.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownCheckboxesExample() -> impl IntoView { let show_status_bar = RwSignal::new(false); let show_activity_bar = RwSignal::new(false); let show_panel = RwSignal::new(false); view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">Open</Button> </DropdownMenuTrigger> <DropdownMenuContent class="w-40"> <DropdownMenuGroup> <DropdownMenuLabel>"Appearance"</DropdownMenuLabel> <DropdownMenuCheckboxItem checked=show_status_bar> "Status Bar" </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked=show_activity_bar disabled=true> "Activity Bar" </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked=show_panel>"Panel"</DropdownMenuCheckboxItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> } }
Checkbox Icons
Icons can be added to checkbox items.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownCheckboxIconsExample() -> impl IntoView { struct Notifications { email: RwSignal<bool>, sms: RwSignal<bool>, push: RwSignal<bool>, } let notifications = Notifications { email: RwSignal::new(true), sms: RwSignal::new(false), push: RwSignal::new(true), }; view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">"Notifications"</Button> </DropdownMenuTrigger> <DropdownMenuContent class="w-48"> <DropdownMenuGroup> <DropdownMenuLabel>"Notification Preferences"</DropdownMenuLabel> <DropdownMenuCheckboxItem checked=notifications .email> {icon!(icondata::LuMail)} "Email notifications" </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked=notifications .sms> {icon!(icondata::LuMessageSquare)} "SMS notifications" </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked=notifications .push> {icon!(icondata::LuBell)} "Push notifications" </DropdownMenuCheckboxItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> } }
Radio Group
Dropdown menus can use RadioGroups.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownRadioGroupExample() -> impl IntoView { let position = RwSignal::new("bottom".to_string()); view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">"Open"</Button> </DropdownMenuTrigger> <DropdownMenuContent class="w-32"> <DropdownMenuGroup> <DropdownMenuLabel>"Panel Position"</DropdownMenuLabel> <DropdownMenuRadioGroup value=position> <DropdownMenuRadioItem value="top">"Top"</DropdownMenuRadioItem> <DropdownMenuRadioItem value="bottom">"Bottom"</DropdownMenuRadioItem> <DropdownMenuRadioItem value="right">"Right"</DropdownMenuRadioItem> </DropdownMenuRadioGroup> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> } }
Radio Icons
Icons can be added to radio items..
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownRadioIconsExample() -> impl IntoView { let payment_method = RwSignal::new("card".to_string()); view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">"Payment Method"</Button> </DropdownMenuTrigger> <DropdownMenuContent class="min-w-56"> <DropdownMenuGroup> <DropdownMenuLabel>"Select Payment Method"</DropdownMenuLabel> <DropdownMenuRadioGroup value=payment_method> <DropdownMenuRadioItem value="card"> {icon!(icondata::LuCreditCard)} "Credit Card" </DropdownMenuRadioItem> <DropdownMenuRadioItem value="paypal"> {icon!(icondata::LuWallet)} "PayPal" </DropdownMenuRadioItem> <DropdownMenuRadioItem value="bank"> {icon!(icondata::LuBuilding2)} "Bank Transfer" </DropdownMenuRadioItem> </DropdownMenuRadioGroup> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> } }
Destructive
Use the destructive variant for irreversible actions.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownDestructiveExample() -> impl IntoView { view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">"Actions"</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuGroup> <DropdownMenuItem>{icon!(icondata::LuPencil)} "Edit"</DropdownMenuItem> <DropdownMenuItem>{icon!(icondata::LuShare)} "Share"</DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem variant="destructive"> {icon!(icondata::LuTrash)} "Delete" </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> } }
Avatar
An account switcher dropdown triggered by an avatar.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownAvatarExample() -> impl IntoView { view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="ghost" size="icon" class="rounded-full"> <Avatar> <AvatarImage src="https://github.com/shadcn.png" alt="shadcn" /> <AvatarFallback>"LR"</AvatarFallback> </Avatar> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end"> <DropdownMenuGroup> <DropdownMenuItem>{icon!(icondata::LuBadgeCheck)} "Account"</DropdownMenuItem> <DropdownMenuItem>{icon!(icondata::LuCreditCard)} "Billing"</DropdownMenuItem> <DropdownMenuItem>{icon!(icondata::LuBell)} "Notifications"</DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuItem>{icon!(icondata::LuLogOut)} "Sign Out"</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> } }
Complex
A richer example combining groups, icons, and submenus.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownComplexExample() -> impl IntoView { let theme = RwSignal::new("light".to_string()); struct Notifications { email: RwSignal<bool>, push: RwSignal<bool>, } let notifications = Notifications { email: RwSignal::new(true), push: RwSignal::new(true), }; view! { <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline">"Complex Menu"</Button> </DropdownMenuTrigger> <DropdownMenuContent class="w-44"> <DropdownMenuGroup> <DropdownMenuLabel>"File"</DropdownMenuLabel> <DropdownMenuItem> {icon!(icondata::LuFile)} "New File" <DropdownMenuShortcut>"⌘N"</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem> {icon!(icondata::LuFolder)} "New Folder" <DropdownMenuShortcut>"⇧⌘N"</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuSub> <DropdownMenuSubTrigger> {icon!(icondata::LuFolderOpen)} "Open Recent" </DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuGroup> <DropdownMenuLabel>"Recent Projects"</DropdownMenuLabel> <DropdownMenuItem> {icon!(icondata::LuFileCode)} "Project Alpha" </DropdownMenuItem> <DropdownMenuItem> {icon!(icondata::LuFileCode)} "Project Beta" </DropdownMenuItem> <DropdownMenuSub> <DropdownMenuSubTrigger> {icon!(icondata::FiMoreHorizontal)} "More Projects" </DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuItem> {icon!(icondata::LuFileCode)} "Project Gamma" </DropdownMenuItem> <DropdownMenuItem> {icon!(icondata::LuFileCode)} "Project Delta" </DropdownMenuItem> </DropdownMenuSubContent> </DropdownMenuSub> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem> {icon!(icondata::LuFolderSearch)} "Browse..." </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuSubContent> </DropdownMenuSub> <DropdownMenuSeparator /> <DropdownMenuItem> {icon!(icondata::LuSave)} "Save" <DropdownMenuShortcut>"⌘S"</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem> {icon!(icondata::LuDownload)} "Export" <DropdownMenuShortcut>"⇧⌘E"</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuLabel>"View"</DropdownMenuLabel> <DropdownMenuCheckboxItem checked=true> {icon!(icondata::LuEye)} "Show Sidebar" </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem> {icon!(icondata::FiLayout)} "Show Status Bar" </DropdownMenuCheckboxItem> <DropdownMenuSub> <DropdownMenuSubTrigger> {icon!(icondata::LuPalette)} "Theme" </DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuGroup> <DropdownMenuLabel>"Appearance"</DropdownMenuLabel> <DropdownMenuRadioGroup value=theme> <DropdownMenuRadioItem value="light"> {icon!(icondata::LuSun)} "Light" </DropdownMenuRadioItem> <DropdownMenuRadioItem value="dark"> {icon!(icondata::LuMoon)} "Dark" </DropdownMenuRadioItem> <DropdownMenuRadioItem value="system"> {icon!(icondata::LuMonitor)} "System" </DropdownMenuRadioItem> </DropdownMenuRadioGroup> </DropdownMenuGroup> </DropdownMenuSubContent> </DropdownMenuSub> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuLabel>"Account"</DropdownMenuLabel> <DropdownMenuItem> {icon!(icondata::LuUser)} "Profile" <DropdownMenuShortcut>"⇧⌘P"</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem>{icon!(icondata::LuCreditCard)} "Billing"</DropdownMenuItem> <DropdownMenuSub> <DropdownMenuSubTrigger> {icon!(icondata::LuSettings)} "Settings" </DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuGroup> <DropdownMenuLabel>"Preferences"</DropdownMenuLabel> <DropdownMenuItem> {icon!(icondata::LuKeyboard)} "Keyboard Shortcuts" </DropdownMenuItem> <DropdownMenuItem> {icon!(icondata::LuLanguages)} "Language" </DropdownMenuItem> <DropdownMenuSub> <DropdownMenuSubTrigger> {icon!(icondata::LuBell)} "Notifications" </DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuGroup> <DropdownMenuLabel>"Notification Types"</DropdownMenuLabel> <DropdownMenuCheckboxItem checked=notifications .push> {icon!(icondata::LuBell)} "Push Notifications" </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked=notifications .email> {icon!(icondata::LuMail)} "Email Notifications" </DropdownMenuCheckboxItem> </DropdownMenuGroup> </DropdownMenuSubContent> </DropdownMenuSub> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem> {icon!(icondata::LuShield)} "Privacy & Security" </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuSubContent> </DropdownMenuSub> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem> {icon!(icondata::FiHelpCircle)} "Help & Support" </DropdownMenuItem> <DropdownMenuItem> {icon!(icondata::LuFileText)} "Documentation" </DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem variant="destructive"> {icon!(icondata::LuLogOut)} "Sign Out" <DropdownMenuShortcut>"⇧⌘Q"</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> } }
Anatomy
Import all parts and piece them together.
use leptos::prelude::*; use singlestage::*; #[component] pub fn DropdownMenuAnatomy() -> impl IntoView { view! { <DropdownMenu> <DropdownMenuTrigger> <Button /> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuGroup> <DropdownMenuLabel /> <DropdownMenuItem> <DropdownMenuShortcut /> </DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuSub> <DropdownMenuSubTrigger /> <DropdownMenuSubContent> <DropdownMenuGroup> <DropdownMenuCheckboxItem /> <DropdownMenuCheckboxItem /> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuRadioGroup> <DropdownMenuRadioItem /> <DropdownMenuRadioItem /> <DropdownMenuRadioItem /> </DropdownMenuRadioGroup> </DropdownMenuGroup> </DropdownMenuSubContent> </DropdownMenuSub> </DropdownMenuContent> </DropdownMenu> } }
API Reference
CheckboxItem
A checkbox item for popover menus.
| Name | Type | Default | Description |
|---|---|---|---|
| disabled | bool | false | Controls whether the item appears disabled or not. |
| dismiss | bool | true | Toggle whether clicking this item dismisses the parent menu. |
| inset | bool | false | Set whether or not this element should display inset from its normal position. |
| variant | String | "" | Set the display variant of the item. Accepted values: "destructive". |
DropdownMenu
Contains all the parts of a dropdown menu.
| Name | Type | Default | Description |
|---|---|---|---|
| modal | bool | false | Set whether or not this popover should be modal meaning it can't be light dismissed Use this along with the `open` signal for a manually managed popover |
| open | bool | false | Reactive signal that can remotely control the open state of the popover **but is not coupled to the actual open state of the popover** unless `modal` is set to `true` |
Label
Labels groups.
| Name | Type | Default | Description |
|---|---|---|---|
| invalid | bool | false | Set whether or not this element should display as invalid. |
MenuContent
The component that pops out when the dropdown menu is open.
| Name | Type | Default | Description |
|---|---|---|---|
| align | String | "start" | Set how to align the popover. Accepted values are "start" | "center" | "end" |
| side | String | "bottom" | Set which side the popover opens relative to the trigger. Accepted values are "top" | "right" | "bottom" | "left". |
MenuItem
Contains a menu item.
| Name | Type | Default | Description |
|---|---|---|---|
| disabled | bool | false | Controls whether the item appears disabled or not. |
| dismiss | bool | true | Toggle whether clicking this item dismisses the parent menu. |
| inset | bool | false | Set whether or not this element should display inset from its normal position. |
| variant | String | "" | Set the display variant of the item. Accepted values are "destructive" |
MenuShortcut
Displays next to the item content and indicates keyboard shortcuts.
MenuSubContent
The component that pops out when the sub trigger is triggered.
MenuSubTrigger
Defines the area where a sub menu can be triggered.
| Name | Type | Default | Description |
|---|---|---|---|
| disabled | bool | false | Controls whether the item appears disabled or not. |
| inset | bool | false | Set whether or not this element should display inset from its normal position. |
| variant | String | "" | Set the display variant of the item. Accepted values: "destructive". |
RadioItem
A radio item for popover menus.
| Name | Type | Default | Description |
|---|---|---|---|
| disabled | bool | false | Controls whether the item appears disabled or not. |
| dismiss | bool | true | Toggle whether clicking this item dismisses the parent menu. |
| inset | bool | false | Set whether or not this element should display inset from its normal position. |
| variant | String | "" | Set the display variant of the item. Accepted values: "destructive". |