Components
Base Components
Dropdown Menu
A menu triggered by a button, with items and optional submenus.
Example
Installation
Install the following dependencies:
npm install radix-uinpm install lucide-reactCreate a dropdown-menu.tsx file and paste the following code into it.
"use client"import * as React from "react"import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"import { cn } from "@/lib/utils"function DropdownMenu({ ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) { return ( <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} /> )}function DropdownMenuTrigger({ ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) { return ( <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} /> )}function DropdownMenuGroup({ ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) { return ( <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} /> )}function DropdownMenuPortal({ ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) { return ( <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} /> )}function DropdownMenuSub({ ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) { return ( <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} /> )}function DropdownMenuRadioGroup({ ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) { return ( <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} /> )}function DropdownMenuSubTrigger({ className, inset, children, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean}) { return ( <DropdownMenuPrimitive.SubTrigger data-slot="dropdown-menu-sub-trigger" data-inset={inset} className={cn( "text-primary focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-primary flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > {children} <ChevronRightIcon className="ml-auto" /> </DropdownMenuPrimitive.SubTrigger> )}function DropdownMenuSubContent({ className, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) { return ( <DropdownMenuPrimitive.SubContent data-slot="dropdown-menu-sub-content" className={cn( "bg-popover text-popover-foreground 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 z-[210] min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", className )} {...props} /> )}function DropdownMenuContent({ className, sideOffset = 4, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Content> & { dir?: React.HTMLAttributes<HTMLDivElement>["dir"]}) { return ( <DropdownMenuPrimitive.Portal> <DropdownMenuPrimitive.Content data-slot="dropdown-menu-content" sideOffset={sideOffset} className={cn( "bg-popover text-popover-foreground 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 z-[210] max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", className )} {...props} /> </DropdownMenuPrimitive.Portal> )}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( "text-primary focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-primary relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function DropdownMenuCheckboxItem({ className, children, checked, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) { return ( <DropdownMenuPrimitive.CheckboxItem data-slot="dropdown-menu-checkbox-item" className={cn( "text-primary focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} checked={checked} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <DropdownMenuPrimitive.ItemIndicator> <CheckIcon className="size-4" /> </DropdownMenuPrimitive.ItemIndicator> </span> {children} </DropdownMenuPrimitive.CheckboxItem> )}function DropdownMenuRadioItem({ className, children, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) { return ( <DropdownMenuPrimitive.RadioItem data-slot="dropdown-menu-radio-item" className={cn( "text-primary focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <DropdownMenuPrimitive.ItemIndicator> <CircleIcon className="size-2 fill-current" /> </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( "text-menu-heading px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className )} {...props} /> )}function DropdownMenuSeparator({ className, ...props}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) { return ( <DropdownMenuPrimitive.Separator data-slot="dropdown-menu-separator" className={cn("bg-border -mx-1 my-1 h-px", className)} {...props} /> )}function DropdownMenuShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="dropdown-menu-shortcut" className={cn( "text-primary ml-auto text-xs tracking-widest", className )} {...props} /> )}export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuGroup, DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuRadioGroup,}Check the import paths to ensure they match your project setup.
Usage
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Open</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuGroup>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Billing</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>Team</DropdownMenuItem>
<DropdownMenuItem>Subscription</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>Examples
Basic
A basic dropdown with labels and separators.Submenu
UseDropdownMenuSub, DropdownMenuSubTrigger, and DropdownMenuSubContent for nested actions.
Shortcuts
AddDropdownMenuShortcut for keyboard hints.
Icons
Pair icons with labels for faster scanning.Checkboxes
UseDropdownMenuCheckboxItem with checked and onCheckedChange for toggles.
Checkboxes with icons
Icons work inside checkbox items as well.Radio group
UseDropdownMenuRadioGroup and DropdownMenuRadioItem for a single choice.
Radio with icons
Radio items can include leading icons (for example payment methods).Destructive
Usevariant="destructive" on DropdownMenuItem for dangerous actions.
Avatar trigger
Use a circular avatar (or icon-only button) as the trigger for an account menu.Complex
Combine groups, icons, a submenu, a checkbox, and a destructive action.RTL
Right-to-left layout: wrap the trigger and menu in a container withdir="rtl".
API Reference
The dropdown composes Radix primitives:DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuGroup, DropdownMenuSub, DropdownMenuSubTrigger, and DropdownMenuSubContent. See the Radix Dropdown Menu documentation for behavior and accessibility.