Displays a menu to the user — such as a set of actions or functions — triggered by a button.
1import { Button } from "@/components/ui/button";2import {3 DropdownMenu,4 DropdownMenuContent,Installation
pnpm dlx sprawlify@latest add dropdown-menunpx sprawlify@latest add dropdown-menuyarn sprawlify@latest add dropdown-menubunx --bun sprawlify@latest add dropdown-menu
Install the following dependencies:
pnpm add @sprawlify/primitives @sprawlify/solidnpm install @sprawlify/primitives @sprawlify/solidyarn add @sprawlify/primitives @sprawlify/solidbun add @sprawlify/primitives @sprawlify/solid
Add the following files to your project:
1import { DropdownMenu as DropdownMenuPrimitive } from "@sprawlify/solid/dropdown-menu";2import { cn } from "@/lib/utils";3import { CheckIcon, ChevronRightIcon } from "lucide-solid";4import type { ComponentProps } from "solid-js";5import { splitProps } from "solid-js";6import { Portal } from "solid-js/web";78function DropdownMenu(props: ComponentProps<typeof DropdownMenuPrimitive.Root>) {9 return (10 <DropdownMenuPrimitive.Root11 data-slot="dropdown-menu"12 positioning={{ offset: { mainAxis: 4 } }}13 {...props}14 />15 );16}1718function DropdownMenuPortal(props: ComponentProps<any>) {19 return <Portal data-slot="dropdown-menu-portal" {...props} />;20}2122function DropdownMenuTrigger(props: ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {23 return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;24}2526function DropdownMenuContent(props: ComponentProps<typeof DropdownMenuPrimitive.Content>) {27 const [local, others] = splitProps(props, ["class"]);28 return (29 <Portal>30 <DropdownMenuPrimitive.Positioner>31 <DropdownMenuPrimitive.Content32 data-slot="dropdown-menu-content"33 class={cn(34 "focus-visible:outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground min-w-32 rounded-lg p-1 shadow-md ring-1 duration-100 z-(--z-index) max-h-(--available-height) w-(--reference-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto data-[state=closed]:overflow-hidden",35 local.class,36 )}37 {...others}38 />39 </DropdownMenuPrimitive.Positioner>40 </Portal>41 );42}4344function DropdownMenuGroup(props: ComponentProps<typeof DropdownMenuPrimitive.ItemGroup>) {45 return <DropdownMenuPrimitive.ItemGroup data-slot="dropdown-menu-group" {...props} />;46}4748function DropdownMenuItem(49 props: ComponentProps<typeof DropdownMenuPrimitive.Item> & {50 inset?: boolean;51 variant?: "default" | "destructive";52 },53) {54 const [local, others] = splitProps(props, ["class", "inset", "variant"]);55 const variant = () => local.variant ?? "default";5657 return (58 <DropdownMenuPrimitive.Item59 data-slot="dropdown-menu-item"60 data-inset={local.inset}61 data-variant={variant()}62 class={cn(63 "gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",64 "data-[variant=destructive]:text-destructive data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[variant=destructive]:data-highlighted:bg-destructive/10 dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 data-[variant=destructive]:data-highlighted:text-destructive data-[variant=destructive]:data-highlighted:*:[svg]:text-destructive not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground",65 local.class,66 )}67 {...others}68 />69 );70}7172function DropdownMenuCheckboxItem(73 props: ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem> & {74 inset?: boolean;75 },76) {77 const [local, others] = splitProps(props, ["class", "children", "checked", "inset"]);7879 return (80 <DropdownMenuPrimitive.CheckboxItem81 data-slot="dropdown-menu-checkbox-item"82 data-inset={local.inset}83 class={cn(84 "focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",85 local.class,86 )}87 checked={local.checked}88 {...others}89 >90 <span91 class="absolute right-2 flex items-center justify-center pointer-events-none"92 data-slot="dropdown-menu-checkbox-item-indicator"93 >94 <DropdownMenuPrimitive.ItemIndicator>95 <CheckIcon />96 </DropdownMenuPrimitive.ItemIndicator>97 </span>98 {local.children}99 </DropdownMenuPrimitive.CheckboxItem>100 );101}102103function DropdownMenuRadioGroup(104 props: ComponentProps<typeof DropdownMenuPrimitive.RadioItemGroup>,105) {106 return <DropdownMenuPrimitive.RadioItemGroup data-slot="dropdown-menu-radio-group" {...props} />;107}108109function DropdownMenuRadioItem(110 props: ComponentProps<typeof DropdownMenuPrimitive.RadioItem> & {111 inset?: boolean;112 },113) {114 const [local, others] = splitProps(props, ["class", "children", "inset"]);115116 return (117 <DropdownMenuPrimitive.RadioItem118 data-slot="dropdown-menu-radio-item"119 data-inset={local.inset}120 class={cn(121 "focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",122 local.class,123 )}124 {...others}125 >126 <span127 class="absolute right-2 flex items-center justify-center pointer-events-none"128 data-slot="dropdown-menu-radio-item-indicator"129 >130 <DropdownMenuPrimitive.ItemIndicator>131 <CheckIcon />132 </DropdownMenuPrimitive.ItemIndicator>133 </span>134 {local.children}135 </DropdownMenuPrimitive.RadioItem>136 );137}138139function DropdownMenuLabel(140 props: ComponentProps<typeof DropdownMenuPrimitive.ItemGroupLabel> & {141 inset?: boolean;142 },143) {144 const [local, others] = splitProps(props, ["class", "inset"]);145146 return (147 <DropdownMenuPrimitive.ItemGroupLabel148 data-slot="dropdown-menu-label"149 data-inset={local.inset}150 class={cn(151 "text-muted-foreground px-1.5 py-1 text-xs font-medium data-inset:pl-7",152 local.class,153 )}154 {...others}155 />156 );157}158159function DropdownMenuSeparator(props: ComponentProps<typeof DropdownMenuPrimitive.Separator>) {160 const [local, others] = splitProps(props, ["class"]);161162 return (163 <DropdownMenuPrimitive.Separator164 data-slot="dropdown-menu-separator"165 class={cn("bg-border -mx-1 my-1 h-px", local.class)}166 {...others}167 />168 );169}170171function DropdownMenuShortcut(props: ComponentProps<"span">) {172 const [local, others] = splitProps(props, ["class"]);173174 return (175 <span176 data-slot="dropdown-menu-shortcut"177 class={cn(178 "text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest",179 local.class,180 )}181 {...others}182 />183 );184}185186function DropdownMenuSub(props: ComponentProps<typeof DropdownMenuPrimitive.Root>) {187 return <DropdownMenuPrimitive.Root data-slot="dropdown-menu-sub" {...props} />;188}189190function DropdownMenuSubTrigger(191 props: ComponentProps<typeof DropdownMenuPrimitive.TriggerItem> & {192 inset?: boolean;193 },194) {195 const [local, others] = splitProps(props, ["class", "inset", "children"]);196197 return (198 <DropdownMenuPrimitive.TriggerItem199 data-slot="dropdown-menu-sub-trigger"200 data-inset={local.inset}201 class={cn(202 "w-full data-[state=open]:bg-accent data-[state=open]:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0",203 local.class,204 )}205 {...others}206 >207 {local.children}208 <ChevronRightIcon class="ml-auto" />209 </DropdownMenuPrimitive.TriggerItem>210 );211}212213function DropdownMenuSubContent(props: ComponentProps<typeof DropdownMenuPrimitive.Content>) {214 const [local, others] = splitProps(props, ["class"]);215216 return (217 <DropdownMenuPrimitive.Positioner>218 <DropdownMenuPrimitive.Content219 data-slot="dropdown-menu-sub-content"220 class={cn(221 "focus-visible:outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground min-w-[96px] rounded-lg p-1 shadow-lg ring-1 duration-100 origin-(--transform-origin) overflow-hidden",222 local.class,223 )}224 {...others}225 />226 </DropdownMenuPrimitive.Positioner>227 );228}229230export {231 DropdownMenu,232 DropdownMenuPortal,233 DropdownMenuTrigger,234 DropdownMenuContent,235 DropdownMenuGroup,236 DropdownMenuLabel,237 DropdownMenuItem,238 DropdownMenuCheckboxItem,239 DropdownMenuRadioGroup,240 DropdownMenuRadioItem,241 DropdownMenuSeparator,242 DropdownMenuShortcut,243 DropdownMenuSub,244 DropdownMenuSubTrigger,245 DropdownMenuSubContent,246};247Update the import paths to match your project setup.
Usage
1import {
2 DropdownMenu,
3 DropdownMenuPortal,
4 DropdownMenuTrigger,
5 DropdownMenuContent,
6 DropdownMenuGroup,
7 DropdownMenuLabel,
8 DropdownMenuItem,
9 DropdownMenuCheckboxItem,
10 DropdownMenuRadioGroup,
11 DropdownMenuRadioItem,
12 DropdownMenuSeparator,
13 DropdownMenuShortcut,
14 DropdownMenuSub,
15 DropdownMenuSubTrigger,
16 DropdownMenuSubContent
17} from "@/components/ui/dropdown-menu"1<DropdownMenu>
2 <DropdownMenuTrigger
3 asChild={(props) => (
4 <Button variant="outline" {...props()}>
5 Open
6 </Button>
7 )}
8 />
9 <DropdownMenuContent>
10 <DropdownMenuGroup>
11 <DropdownMenuLabel>My Account</DropdownMenuLabel>
12 <DropdownMenuItem>Profile</DropdownMenuItem>
13 <DropdownMenuItem>Billing</DropdownMenuItem>
14 </DropdownMenuGroup>
15 <DropdownMenuSeparator />
16 <DropdownMenuGroup>
17 <DropdownMenuItem>Team</DropdownMenuItem>
18 <DropdownMenuItem>Subscription</DropdownMenuItem>
19 </DropdownMenuGroup>
20 </DropdownMenuContent>
21</DropdownMenu>Examples
Avatar
An account switcher dropdown triggered by an avatar.
1import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";2import { Button } from "@/components/ui/button";3import {4 DropdownMenu,Basic
A basic dropdown menu with labels and separators.
1import { Button } from "@/components/ui/button";2import {3 DropdownMenu,4 DropdownMenuContent,Checkboxes
Use DropdownMenuCheckboxItem for toggles.
1import { createSignal } from "solid-js";2import { Button } from "@/components/ui/button";3import {4 DropdownMenu,Checkboxes Icons
Add icons to checkbox items.
1import { createSignal } from "solid-js";2import { Button } from "@/components/ui/button";3import {4 DropdownMenu,Complex
A richer example combining groups, icons, and submenus.
1import { createSignal } from "solid-js";2import { Button } from "@/components/ui/button";3import {4 DropdownMenu,Destructive
Use variant="destructive" for irreversible actions.
1import { Button } from "@/components/ui/button";2import {3 DropdownMenu,4 DropdownMenuContent,Icons
Combine icons with labels for quick scanning.
1import { Button } from "@/components/ui/button";2import {3 DropdownMenu,4 DropdownMenuContent,Radio Icons
Show radio options with icons.
1import { createSignal } from "solid-js";2import { Button } from "@/components/ui/button";3import {4 DropdownMenu,Shortcuts
Add DropdownMenuShortcut to show keyboard hints.
1import { Button } from "@/components/ui/button";2import {3 DropdownMenu,4 DropdownMenuContent,Submenu
Use DropdownMenuSub to nest secondary actions.
1import { Button } from "@/components/ui/button";2import {3 DropdownMenu,4 DropdownMenuContent,On This Page
InstallationUsageExamplesAvatarBasicCheckboxesCheckboxes IconsComplexDestructiveIconsRadio IconsShortcutsSubmenuGet PRO
Need premium blocks and templates? Upgrade to PRO and get access.