import { Button } from "@/registry/components/button/react/button";import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger,} from "@/registry/components/dropdown-menu/react/dropdown-menu";
export function DropdownMenuDemo() { return ( <DropdownMenu> <DropdownMenuTrigger asChild> <Button data-variant="outline">Open</Button> </DropdownMenuTrigger> <DropdownMenuContent align="start"> <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> <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> </DropdownMenuContent> </DropdownMenu> );}
You can add the dropdown menu component in two ways:
- During project initialization:
pnpm dlx make-sugarcube@latest init
npx make-sugarcube@latest init
yarn make-sugarcube@latest init
bunx --bun make-sugarcube@latest init
Then select the dropdown menu component when prompted.
- After initialization:
pnpm dlx make-sugarcube@latest components dropdown-menu --framework react
npx make-sugarcube@latest components dropdown-menu --framework react
yarn make-sugarcube@latest components dropdown-menu --framework react
bunx --bun make-sugarcube@latest components dropdown-menu --framework react
When you add the dropdown menu component, you get:
dropdown-menu.tsx
- The React component implementationdropdown-menu.css
- The base styles for the dropdown menu@clsx
- Theclsx
utility for conditional classes
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";import cn from "clsx";import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) { return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;}
function DropdownMenuPortal({ ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) { return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;}
function DropdownMenuTrigger({ ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) { return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;}
function DropdownMenuContent({ className, sideOffset = 4, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) { return ( <DropdownMenuPrimitive.Portal> <DropdownMenuPrimitive.Content data-slot="dropdown-menu-content" sideOffset={sideOffset} className={cn("dropdown-menu-content", className)} {...props} /> </DropdownMenuPrimitive.Portal> );}
function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) { return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;}
function DropdownMenuItem({ className, inset, variant = "default", ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & { inset?: boolean; variant?: "default" | "destructive";}) { return ( <DropdownMenuPrimitive.Item data-slot="dropdown-menu-item" data-inset={inset} data-variant={variant} className={cn("dropdown-menu-item", className)} {...props} /> );}
function DropdownMenuCheckboxItem({ className, children, checked, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem> & { checked?: boolean;}) { return ( <DropdownMenuPrimitive.CheckboxItem data-slot="dropdown-menu-checkbox-item" className={cn("dropdown-menu-item", className)} {...(checked !== undefined && { checked })} {...props} > <span className="dropdown-menu-item-indicator"> <DropdownMenuPrimitive.ItemIndicator> <CheckIcon className="check" /> </DropdownMenuPrimitive.ItemIndicator> </span> {children} </DropdownMenuPrimitive.CheckboxItem> );}
function DropdownMenuRadioGroup({ ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) { return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;}
function DropdownMenuRadioItem({ className, children, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) { return ( <DropdownMenuPrimitive.RadioItem data-slot="dropdown-menu-radio-item" className={cn("dropdown-menu-item", className)} {...props} > <span className="dropdown-menu-item-indicator"> <DropdownMenuPrimitive.ItemIndicator> <CircleIcon /> </DropdownMenuPrimitive.ItemIndicator> </span> {children} </DropdownMenuPrimitive.RadioItem> );}
function DropdownMenuLabel({ className, inset, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & { inset?: boolean;}) { return ( <DropdownMenuPrimitive.Label data-slot="dropdown-menu-label" data-inset={inset} className={cn("dropdown-menu-label", className)} {...props} /> );}
function DropdownMenuSeparator({ className, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) { return ( <DropdownMenuPrimitive.Separator data-slot="dropdown-menu-separator" className={cn("dropdown-menu-separator", className)} {...props} /> );}
function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) { return <span className={cn("dropdown-menu-shortcut", className)} {...props} />;}
function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) { return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;}
function DropdownMenuSubTrigger({ className, inset, children, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean;}) { return ( <DropdownMenuPrimitive.SubTrigger data-inset={inset} className={cn("dropdown-menu-item", className)} {...props} > {children} <ChevronRightIcon className="" /> </DropdownMenuPrimitive.SubTrigger> );}
function DropdownMenuSubContent({ className, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) { return ( <DropdownMenuPrimitive.SubContent data-slot="dropdown-menu-sub-content" className={cn("dropdown-menu-content", className)} style={{ "--dropdown-width": 0 } as React.CSSProperties} {...props} /> );}
export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent,};
.dropdown-menu-content { overflow-x: hidden; overflow-y: auto; min-width: 8rem; background-color: var(--dropdown-bg); color: var(--dropdown-fg); width: var(--dropdown-width); border-radius: var(--dropdown-radius); border-style: var(--dropdown-border-style); border-width: var(--dropdown-border-width); border-color: var(--dropdown-border-color); padding: var(--dropdown-padding); box-shadow: var(--dropdown-shadow); transform-origin: var(--radix-dropdown-menu-content-transform-origin); max-height: var(--radix-dropdown-menu-content-available-height);}
.dropdown-menu-item { position: relative; display: flex; cursor: default; align-items: center; justify-content: space-between; user-select: none; outline: none; gap: var(--dropdown-item-gap); border-radius: var(--dropdown-item-radius); padding-block: var(--dropdown-item-padding-block); padding-inline: var(--dropdown-item-padding-inline); font-size: var(--dropdown-item-font-size); transition: background-color var(--dropdown-item-transition-duration) var(--dropdown-item-transition-ease);
&svg { pointer-events: none; }}
.dropdown-menu-item:hover { background-color: var(--dropdown-item-bg-hover);}
.dropdown-menu-item[data-disabled] { color: var(--text-disabled); pointer-events: none;}
[data-slot="dropdown-menu-checkbox-item"],[data-slot="dropdown-menu-radio-item"] { padding-inline-start: 32px;}
.dropdown-menu-item-indicator { position: absolute; left: 8px; pointer-events: none; display: flex; align-items: center; justify-content: center;}
[data-slot="dropdown-menu-radio-item"] .dropdown-menu-item-indicator svg { width: calc(var(--dropdown-item-indicator-size) / 2); height: calc(var(--dropdown-item-indicator-size) / 2); fill: currentColor;}
.dropdown-menu-label { padding-block: var(--dropdown-label-padding-block); padding-inline: var(--dropdown-label-padding-inline); font-size: var(--dropdown-label-font-size); font-weight: var(--dropdown-label-font-weight);}
.dropdown-menu-separator { height: var(--dropdown-separator-height); background-color: var(--dropdown-separator-bg); margin-block: var(--space-3xs); margin-inline: calc(-1 * var(--dropdown-padding));}
.dropdown-menu-shortcut { color: var(--text-muted); letter-spacing: var(--tracking-wide);}
[data-slot="dropdown-menu-content"][data-state="open"][data-side="bottom"],[data-slot="dropdown-menu-sub-content"][data-state="open"][data-side="bottom"] { animation: dropdown-open-bottom var(--duration-150) var(--ease-out);}
[data-slot="dropdown-menu-content"][data-state="open"][data-side="top"],[data-slot="dropdown-menu-sub-content"][data-state="open"][data-side="top"] { animation: dropdown-open-top var(--duration-150) var(--ease-out);}
[data-slot="dropdown-menu-content"][data-state="open"][data-side="left"],[data-slot="dropdown-menu-sub-content"][data-state="open"][data-side="left"] { animation: dropdown-open-left var(--duration-150) var(--ease-out);}
[data-slot="dropdown-menu-content"][data-state="open"][data-side="right"],[data-slot="dropdown-menu-sub-content"][data-state="open"][data-side="right"] { animation: dropdown-open-right var(--duration-150) var(--ease-out);}
[data-slot="dropdown-menu-content"][data-state="closed"][data-side="bottom"],[data-slot="dropdown-menu-sub-content"][data-state="closed"][data-side="bottom"] { animation: dropdown-closed-bottom var(--duration-150) var(--ease-out);}
[data-slot="dropdown-menu-content"][data-state="closed"][data-side="top"],[data-slot="dropdown-menu-sub-content"][data-state="closed"][data-side="top"] { animation: dropdown-closed-top var(--duration-150) var(--ease-out);}
[data-slot="dropdown-menu-content"][data-state="closed"][data-side="left"],[data-slot="dropdown-menu-sub-content"][data-state="closed"][data-side="left"] { animation: dropdown-closed-left var(--duration-150) var(--ease-out);}
[data-slot="dropdown-menu-content"][data-state="closed"][data-side="right"],[data-slot="dropdown-menu-sub-content"][data-state="closed"][data-side="right"] { animation: dropdown-closed-right var(--duration-150) var(--ease-out);}
@keyframes dropdown-open-bottom { from { opacity: 0; transform: scale(0.95) translateY(-8px); } to { opacity: 1; transform: scale(1) translateY(0); }}
@keyframes dropdown-open-top { from { opacity: 0; transform: scale(0.95) translateY(8px); } to { opacity: 1; transform: scale(1) translateY(0); }}
@keyframes dropdown-open-left { from { opacity: 0; transform: scale(0.95) translateX(8px); } to { opacity: 1; transform: scale(1) translateX(0); }}
@keyframes dropdown-open-right { from { opacity: 0; transform: scale(0.95) translateX(-8px); } to { opacity: 1; transform: scale(1) translateX(0); }}
@keyframes dropdown-closed-bottom { from { opacity: 1; transform: scale(1) translateY(0); } to { opacity: 0; transform: scale(0.95) translateY(-8px); }}
@keyframes dropdown-closed-top { from { opacity: 1; transform: scale(1) translateY(0); } to { opacity: 0; transform: scale(0.95) translateY(8px); }}
@keyframes dropdown-closed-left { from { opacity: 1; transform: scale(1) translateX(0); } to { opacity: 0; transform: scale(0.95) translateX(-8px); }}
@keyframes dropdown-closed-right { from { opacity: 1; transform: scale(1) translateX(0); } to { opacity: 0; transform: scale(0.95) translateX(8px); }}
:::note The following examples show how to use data attributes for variants. This is just one possible approach. See the principles section for more information on how to approach component variants. :::
import * as React from "react";
import { Button } from "@/registry/components/button/react/button.tsx";import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger,} from "@/registry/components/dropdown-menu/react/dropdown-menu.tsx";
export default function DropdownMenuCheckboxes() { const [showStatusBar, setShowStatusBar] = React.useState<boolean>(true); const [showActivityBar, setShowActivityBar] = React.useState<boolean>(false); const [showPanel, setShowPanel] = React.useState<boolean>(false);
return ( <DropdownMenu> <DropdownMenuTrigger asChild> <Button data-variant="outline">Open</Button> </DropdownMenuTrigger> <DropdownMenuContent className="w-56"> <DropdownMenuLabel>Appearance</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuCheckboxItem checked={showStatusBar} onCheckedChange={setShowStatusBar} > Status Bar </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked={showActivityBar} onCheckedChange={setShowActivityBar} disabled > Activity Bar </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked={showPanel} onCheckedChange={setShowPanel}> Panel </DropdownMenuCheckboxItem> </DropdownMenuContent> </DropdownMenu> );}
"use client";
import * as React from "react";
import { Button } from "@/registry/components/button/react/button.tsx";import { DropdownMenu, DropdownMenuContent, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger,} from "@/registry/components/dropdown-menu/react/dropdown-menu.tsx";
export function DropdownMenuRadioGroupDemo() { const [position, setPosition] = React.useState("bottom");
return ( <DropdownMenu> <DropdownMenuTrigger asChild> <Button data-variant="outline">Open</Button> </DropdownMenuTrigger> <DropdownMenuContent className="w-56"> <DropdownMenuLabel>Panel Position</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuRadioGroup value={position} onValueChange={setPosition}> <DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem> <DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem> <DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem> </DropdownMenuRadioGroup> </DropdownMenuContent> </DropdownMenu> );}