Dropdown Menu

Accessible dropdown menu with items, checkboxes, radio groups, and submenus. Install Dropdown Menu from the VLLNT UI registry with the shadcn CLI.

Report a bug

Accessible dropdown menu with items, checkboxes, radio groups, and submenus. Part of the overlay family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Installation

Add Dropdown Menu to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/dropdown-menu.json

Source

"use client"; import * as React from "react"; import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; import { cn } from "../../lib/utils"; const DropdownMenu = DropdownMenuPrimitive.Root; const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; const DropdownMenuGroup = DropdownMenuPrimitive.Group; const DropdownMenuPortal = DropdownMenuPrimitive.Portal; const DropdownMenuSub = DropdownMenuPrimitive.Sub; const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; const DropdownMenuSubTrigger = ({ children, className, inset, ref: reference, ...props }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean; ref?: React.Ref<React.ComponentRef<typeof DropdownMenuPrimitive.SubTrigger>>; }) => ( <DropdownMenuPrimitive.SubTrigger className={cn( "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent", inset && "pl-8", className, )} ref={reference} {...props} > {children} <span className="ml-auto text-xs"></span> </DropdownMenuPrimitive.SubTrigger> ); DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName; const DropdownMenuSubContent = ({ className, ref: reference, ...props }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> & { ref?: React.Ref<React.ComponentRef<typeof DropdownMenuPrimitive.SubContent>>; }) => ( <DropdownMenuPrimitive.SubContent className={cn( "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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", className, )} ref={reference} {...props} /> ); DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName; const DropdownMenuContent = ({ className, ref: reference, sideOffset = 4, ...props }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> & { ref?: React.Ref<React.ComponentRef<typeof DropdownMenuPrimitive.Content>>; }) => ( <DropdownMenuPrimitive.Portal> <DropdownMenuPrimitive.Content className={cn( "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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", className, )} ref={reference} sideOffset={sideOffset} {...props} /> </DropdownMenuPrimitive.Portal> ); DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; const DropdownMenuItem = ({ className, inset, ref: reference, ...props }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { inset?: boolean; ref?: React.Ref<React.ComponentRef<typeof DropdownMenuPrimitive.Item>>; }) => ( <DropdownMenuPrimitive.Item className={cn( "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", inset && "pl-8", className, )} ref={reference} {...props} /> ); DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; const DropdownMenuCheckboxItem = ({ checked, children, className, ref: reference, ...props }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> & { ref?: React.Ref< React.ComponentRef<typeof DropdownMenuPrimitive.CheckboxItem> >; }) => ( <DropdownMenuPrimitive.CheckboxItem checked={checked} className={cn( "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className, )} ref={reference} {...props} > <span className="absolute left-2 flex size-3.5 items-center justify-center"> <DropdownMenuPrimitive.ItemIndicator> <span className="text-xs"></span> </DropdownMenuPrimitive.ItemIndicator> </span> {children} </DropdownMenuPrimitive.CheckboxItem> ); DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName; const DropdownMenuRadioItem = ({ children, className, ref: reference, ...props }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> & { ref?: React.Ref<React.ComponentRef<typeof DropdownMenuPrimitive.RadioItem>>; }) => ( <DropdownMenuPrimitive.RadioItem className={cn( "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className, )} ref={reference} {...props} > <span className="absolute left-2 flex size-3.5 items-center justify-center"> <DropdownMenuPrimitive.ItemIndicator> <span className="text-xs"></span> </DropdownMenuPrimitive.ItemIndicator> </span> {children} </DropdownMenuPrimitive.RadioItem> ); DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; const DropdownMenuLabel = ({ className, inset, ref: reference, ...props }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { inset?: boolean; ref?: React.Ref<React.ComponentRef<typeof DropdownMenuPrimitive.Label>>; }) => ( <DropdownMenuPrimitive.Label className={cn( "px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className, )} ref={reference} {...props} /> ); DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; const DropdownMenuSeparator = ({ className, ref: reference, ...props }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> & { ref?: React.Ref<React.ComponentRef<typeof DropdownMenuPrimitive.Separator>>; }) => ( <DropdownMenuPrimitive.Separator className={cn("-m-1 h-px bg-muted", className)} ref={reference} {...props} /> ); DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => { return ( <span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} /> ); }; DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, };

Stories

Explore every variant and state in the interactive Storybook:

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Dependencies

  • @vllnt/ui@^0.3.0