Displays a menu to the user — such as a set of actions or functions — triggered by a button.
import { Button } from "~/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger
} from "~/components/ui/dropdown-menu"
export function DropdownMenuDemo() {
return (
<DropdownMenu placement="bottom-start">
<DropdownMenuTrigger as={Button<"button">} variant="outline">
Open
</DropdownMenuTrigger>
<DropdownMenuContent class="w-56">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuGroup>
<DropdownMenuItem>
Profile
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Billing
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Settings
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Keyboard shortcuts
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>Team</DropdownMenuItem>
<DropdownMenuSub overlap>
<DropdownMenuSubTrigger>Invite users</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuItem>Email</DropdownMenuItem>
<DropdownMenuItem>Message</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>More...</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
<DropdownMenuItem>
New Team
<DropdownMenuShortcut>⌘+T</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>GitHub</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuItem disabled>API</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
Log out
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem variant="destructive">
Delete Account
<DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
Installation
CLI
Manual
Install the following dependencies:
Copy and paste the following code into your project.
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"import { mergeProps, splitProps } from "solid-js"
import * as DropdownMenuPrimitive from "@kobalte/core/dropdown-menu"import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/lib/utils"
const DropdownMenu: Component<DropdownMenuPrimitive.DropdownMenuRootProps> = (props) => { return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" gutter={4} {...props} />}
const DropdownMenuPortal: Component<DropdownMenuPrimitive.DropdownMenuPortalProps> = (props) => ( <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />)
const DropdownMenuTrigger = <T extends ValidComponent = "button">( props: PolymorphicProps<T, DropdownMenuPrimitive.DropdownMenuTriggerProps<T>>) => <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
type DropdownMenuContentProps<T extends ValidComponent = "div"> = DropdownMenuPrimitive.DropdownMenuContentProps<T> & { class?: string | undefined }
const DropdownMenuContent = <T extends ValidComponent = "div">( props: PolymorphicProps<T, DropdownMenuContentProps<T>>) => { const [local, others] = splitProps(props as DropdownMenuContentProps, ["class"]) return ( <DropdownMenuPrimitive.Portal> <DropdownMenuPrimitive.Content class={cn( "data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95 z-50 max-h-(--kb-popper-content-available-height) min-w-[8rem] origin-(--kb-menu-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[closed]:animate-out data-[expanded]:animate-in", local.class )} data-slot="dropdown-menu-content" {...others} /> </DropdownMenuPrimitive.Portal> )}
const DropdownMenuGroup: Component<DropdownMenuPrimitive.DropdownMenuGroupProps> = (props) => ( <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />)
type DropdownMenuItemProps<T extends ValidComponent = "div"> = DropdownMenuPrimitive.DropdownMenuItemProps<T> & { class?: string | undefined variant?: "default" | "destructive" }
const DropdownMenuItem = <T extends ValidComponent = "div">( rawProps: PolymorphicProps<T, DropdownMenuItemProps<T>>) => { const props = mergeProps({ variant: "default" }, rawProps) const [local, others] = splitProps(props as DropdownMenuItemProps, ["class", "variant"]) return ( <DropdownMenuPrimitive.Item class={cn( "data-[variant=destructive]:*:[svg]:!text-destructive relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[disabled]:opacity-50 data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0", local.class )} data-slot="dropdown-menu-item" data-variant={local.variant} {...others} /> )}
const DropdownMenuShortcut: Component<ComponentProps<"span">> = (props) => { const [local, others] = splitProps(props, ["class"]) return ( <span class={cn("ml-auto text-muted-foreground text-xs tracking-widest", local.class)} data-slot="dropdown-menu-shortcut" {...others} /> )}
const DropdownMenuLabel: Component<ComponentProps<"div"> & { inset?: boolean }> = (props) => { const [local, others] = splitProps(props, ["class", "inset"]) return ( <div class={cn("px-2 py-1.5 font-medium text-sm data-[inset]:pl-8", local.class)} data-inset={local.inset} data-slot="dropdown-menu-label" {...others} /> )}
type DropdownMenuSeparatorProps<T extends ValidComponent = "hr"> = DropdownMenuPrimitive.DropdownMenuSeparatorProps<T> & { class?: string | undefined }
const DropdownMenuSeparator = <T extends ValidComponent = "hr">( props: PolymorphicProps<T, DropdownMenuSeparatorProps<T>>) => { const [local, others] = splitProps(props as DropdownMenuSeparatorProps, ["class"]) return ( <DropdownMenuPrimitive.Separator class={cn("-mx-1 my-1 h-px bg-border", local.class)} data-slot="dropdown-menu-separator" {...others} /> )}
const DropdownMenuSub: Component<DropdownMenuPrimitive.DropdownMenuSubProps> = (props) => ( <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />)
type DropdownMenuSubTriggerProps<T extends ValidComponent = "div"> = DropdownMenuPrimitive.DropdownMenuSubTriggerProps<T> & { class?: string | undefined children?: JSX.Element }
const DropdownMenuSubTrigger = <T extends ValidComponent = "div">( props: PolymorphicProps<T, DropdownMenuSubTriggerProps<T>>) => { const [local, others] = splitProps(props as DropdownMenuSubTriggerProps, ["class", "children"]) return ( <DropdownMenuPrimitive.SubTrigger class={cn( "flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[expanded]:bg-accent data-[inset]:pl-8 data-[expanded]:text-accent-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0", local.class )} data-slot="dropdown-menu-sub-trigger" {...others} > {local.children} <svg class="ml-auto size-4" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" > <path d="M9 6l6 6l-6 6" /> </svg> </DropdownMenuPrimitive.SubTrigger> )}
type DropdownMenuSubContentProps<T extends ValidComponent = "div"> = DropdownMenuPrimitive.DropdownMenuSubContentProps<T> & { class?: string | undefined }
const DropdownMenuSubContent = <T extends ValidComponent = "div">( props: PolymorphicProps<T, DropdownMenuSubContentProps<T>>) => { const [local, others] = splitProps(props as DropdownMenuSubContentProps, ["class"]) return ( <DropdownMenuPrimitive.SubContent class={cn( "data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95 z-50 min-w-[8rem] origin-(--kb-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[closed]:animate-out data-[expanded]:animate-in", local.class )} data-slot="dropdown-menu-sub-content" {...others} /> )}
type DropdownMenuCheckboxItemProps<T extends ValidComponent = "div"> = DropdownMenuPrimitive.DropdownMenuCheckboxItemProps<T> & { class?: string | undefined children?: JSX.Element }
const DropdownMenuCheckboxItem = <T extends ValidComponent = "div">( props: PolymorphicProps<T, DropdownMenuCheckboxItemProps<T>>) => { const [local, others] = splitProps(props as DropdownMenuCheckboxItemProps, ["class", "children"]) return ( <DropdownMenuPrimitive.CheckboxItem class={cn( "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", local.class )} data-slot="dropdown-menu-checkbox-item" {...others} > <span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <DropdownMenuPrimitive.ItemIndicator> <svg class="size-4" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" > <path d="M5 12l5 5l10 -10" /> </svg> </DropdownMenuPrimitive.ItemIndicator> </span> {local.children} </DropdownMenuPrimitive.CheckboxItem> )}
type DropdownMenuGroupLabelProps<T extends ValidComponent = "span"> = DropdownMenuPrimitive.DropdownMenuGroupLabelProps<T> & { class?: string | undefined }
const DropdownMenuGroupLabel = <T extends ValidComponent = "span">( props: PolymorphicProps<T, DropdownMenuGroupLabelProps<T>>) => { const [local, others] = splitProps(props as DropdownMenuGroupLabelProps, ["class"]) return ( <DropdownMenuPrimitive.GroupLabel class={cn("px-2 py-1.5 font-semibold text-sm", local.class)} data-slot="dropdown-menu-group-label" {...others} /> )}
type DropdownMenuRadioGroupProps< T extends ValidComponent = "div", TValue = string> = DropdownMenuPrimitive.DropdownMenuRadioGroupProps<T, TValue>
const DropdownMenuRadioGroup = <T extends ValidComponent = "div", TValue = string>( props: PolymorphicProps<T, DropdownMenuRadioGroupProps<T, TValue>>) => <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />
type DropdownMenuRadioItemProps<T extends ValidComponent = "div"> = DropdownMenuPrimitive.DropdownMenuRadioItemProps<T> & { class?: string | undefined children?: JSX.Element }
const DropdownMenuRadioItem = <T extends ValidComponent = "div">( props: PolymorphicProps<T, DropdownMenuRadioItemProps<T>>) => { const [local, others] = splitProps(props as DropdownMenuRadioItemProps, ["class", "children"]) return ( <DropdownMenuPrimitive.RadioItem class={cn( "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", local.class )} data-slot="dropdown-menu-radio-item" {...others} > <span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <DropdownMenuPrimitive.ItemIndicator> <svg class="size-2 fill-current" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" > <path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /> </svg> </DropdownMenuPrimitive.ItemIndicator> </span> {local.children} </DropdownMenuPrimitive.RadioItem> )}
export { DropdownMenu, DropdownMenuTrigger, DropdownMenuPortal, DropdownMenuContent, DropdownMenuItem, DropdownMenuShortcut, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, DropdownMenuCheckboxItem, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem}Usage
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger,} from "~/components/ui/dropdown-menu";<DropdownMenu> <DropdownMenuTrigger>Open</DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>My Account</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuItem>Profile</DropdownMenuItem> <DropdownMenuItem>Billing</DropdownMenuItem> <DropdownMenuItem>Team</DropdownMenuItem> <DropdownMenuItem>Subscription</DropdownMenuItem> </DropdownMenuContent></DropdownMenu>Examples
Checkboxes
Radio Group
Dialog
This example shows how to open a dialog from a dropdown menu.
Use modal={false} on the DropdownMenu component.
<DropdownMenu modal={false}> <DropdownMenuTrigger as={Button<"button">} variant="outline"> Actions </DropdownMenuTrigger></DropdownMenu>