MC Dropdown Menu
A menu that appears when triggered by a button click.
Overview
The mc-dropdown-menu component is a flexible action menu for contextual commands, account menus,
shortcuts, nested actions, and selectable menu items. Built on the @base-ui/react menu
primitive, it provides accessible keyboard navigation, focus management, submenus, and controlled
open state handling with MicroClub styling and theme tokens.
As part of the MicroClub UI library, it is designed for instant deployment into your codebase, so you keep full control over the visual language and interactions.
Preview
'use client';import { Button } from '@/components/ui/button';import { McDropdownMenu, McDropdownMenuContent, McDropdownMenuGroup, McDropdownMenuItem, McDropdownMenuLabel, McDropdownMenuPortal, McDropdownMenuSeparator, McDropdownMenuSub, McDropdownMenuSubContent, McDropdownMenuSubTrigger, McDropdownMenuTrigger,} from '@/registry/ui/mc-dropdown-menu';export default function McDropdownMenuDemo() { return ( <McDropdownMenu> <McDropdownMenuTrigger render={ <Button className="w-17 h-10 aria-expanded:shadow-[0_0_0_4px_var(--muted)] aria-expanded:text-primary" variant="outline" > Open </Button> } /> <McDropdownMenuContent className="w-56"> <McDropdownMenuGroup className="h-auto"> <McDropdownMenuLabel>My Account</McDropdownMenuLabel> <McDropdownMenuItem>Profile</McDropdownMenuItem> <McDropdownMenuItem>Settings</McDropdownMenuItem> </McDropdownMenuGroup> <McDropdownMenuSeparator /> <McDropdownMenuGroup className="h-auto"> <McDropdownMenuItem>Team</McDropdownMenuItem> <McDropdownMenuSub> <McDropdownMenuSubTrigger>Invite users</McDropdownMenuSubTrigger> <McDropdownMenuPortal> <McDropdownMenuSubContent> <McDropdownMenuItem>Email</McDropdownMenuItem> <McDropdownMenuItem>Message</McDropdownMenuItem> <McDropdownMenuSeparator /> <McDropdownMenuItem>More...</McDropdownMenuItem> </McDropdownMenuSubContent> </McDropdownMenuPortal> </McDropdownMenuSub> </McDropdownMenuGroup> <McDropdownMenuSeparator /> <McDropdownMenuGroup className="h-auto"> <McDropdownMenuItem variant="destructive">Log out</McDropdownMenuItem> </McDropdownMenuGroup> </McDropdownMenuContent> </McDropdownMenu> );}Add to Your Project
Deploy mc-dropdown-menu directly to your components/ui directory:
Manual Setup
If you prefer manual control:
'use client';import * as React from 'react';import { Menu as MenuPrimitive } from '@base-ui/react/menu';import { CheckIcon, ChevronRightIcon } from 'lucide-react';import { cn } from '@/lib/utils';function McDropdownMenu({ ...props }: MenuPrimitive.Root.Props) { return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />;}function McDropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) { return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;}function McDropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) { return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;}function McDropdownMenuContent({ align = 'start', alignOffset = 0, side = 'bottom', sideOffset = 4, className, ...props}: MenuPrimitive.Popup.Props & Pick<MenuPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>) { return ( <MenuPrimitive.Portal> <MenuPrimitive.Positioner className="isolate z-50 outline-none" align={align} alignOffset={alignOffset} side={side} sideOffset={sideOffset} > <MenuPrimitive.Popup data-slot="dropdown-menu-content" className={cn( 'z-50 w-56 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1 text-popover-foreground opacity-100 shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95', className )} {...props} /> </MenuPrimitive.Positioner> </MenuPrimitive.Portal> );}function McDropdownMenuGroup({ className, ...props}: MenuPrimitive.Group.Props & { className?: string }) { return ( <MenuPrimitive.Group data-slot="dropdown-menu-group" className={cn('w-56 h-[100px] opacity-100 pt-1 px-1', className)} {...props} /> );}function McDropdownMenuLabel({ className, inset, ...props}: MenuPrimitive.GroupLabel.Props & { inset?: boolean;}) { return ( <MenuPrimitive.GroupLabel data-slot="dropdown-menu-label" data-inset={inset} className={cn( 'font-sans font-semibold text-sm leading-5 px-1.5 py-1 text-foreground data-inset:pl-7', className )} {...props} /> );}function McDropdownMenuItem({ className, inset, variant = 'default', ...props}: MenuPrimitive.Item.Props & { inset?: boolean; variant?: 'default' | 'destructive';}) { return ( <MenuPrimitive.Item data-slot="dropdown-menu-item" data-inset={inset} data-variant={variant} className={cn( 'group/dropdown-menu-item relative flex cursor-default items-center gap-2 w-[208px] h-8 opacity-100 rounded-sm px-2 py-1.5 font-sans font-normal text-sm leading-5 outline-hidden select-none data-inset:pl-7 hover:bg-muted hover:text-muted-foreground focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4 data-[variant=destructive]:*:[svg]:text-destructive', className )} {...props} /> );}function McDropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) { return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;}function McDropdownMenuSubTrigger({ className, inset, children, ...props}: MenuPrimitive.SubmenuTrigger.Props & { inset?: boolean;}) { return ( <MenuPrimitive.SubmenuTrigger data-slot="dropdown-menu-sub-trigger" data-inset={inset} className={cn( "flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > {children} <ChevronRightIcon className="cn-rtl-flip ml-auto" /> </MenuPrimitive.SubmenuTrigger> );}function McDropdownMenuSubContent({ align = 'start', alignOffset = -3, side = 'right', sideOffset = 0, className, ...props}: React.ComponentProps<typeof McDropdownMenuContent>) { return ( <McDropdownMenuContent data-slot="dropdown-menu-sub-content" className={cn( 'w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95', className )} align={align} alignOffset={alignOffset} side={side} sideOffset={sideOffset} {...props} /> );}function McDropdownMenuCheckboxItem({ className, children, checked, inset, ...props}: MenuPrimitive.CheckboxItem.Props & { inset?: boolean;}) { return ( <MenuPrimitive.CheckboxItem data-slot="dropdown-menu-checkbox-item" data-inset={inset} className={cn( 'relative flex w-[216px] h-8 cursor-default items-center gap-2 rounded-sm px-2 py-1.5 font-sans text-sm leading-5 font-normal text-muted-foreground outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4', className )} checked={checked} {...props} > <span className="pointer-events-none mr-2 flex items-center justify-center w-4" data-slot="dropdown-menu-checkbox-item-indicator" > <MenuPrimitive.CheckboxItemIndicator> <CheckIcon /> </MenuPrimitive.CheckboxItemIndicator> </span> <span className="truncate">{children}</span> </MenuPrimitive.CheckboxItem> );}function McDropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) { return <MenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;}function McDropdownMenuRadioItem({ className, children, inset, ...props}: MenuPrimitive.RadioItem.Props & { inset?: boolean;}) { return ( <MenuPrimitive.RadioItem data-slot="dropdown-menu-radio-item" data-inset={inset} className={cn( "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 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 flex items-center justify-center" data-slot="dropdown-menu-radio-item-indicator" > <MenuPrimitive.RadioItemIndicator> <CheckIcon /> </MenuPrimitive.RadioItemIndicator> </span> <span>{children}</span> </MenuPrimitive.RadioItem> );}function McDropdownMenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props) { return ( <MenuPrimitive.Separator data-slot="dropdown-menu-separator" className={cn('-mx-1 my-1 h-px bg-border', className)} {...props} /> );}function McDropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) { return ( <span data-slot="dropdown-menu-shortcut" className={cn( 'ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground', className )} {...props} /> );}export { McDropdownMenu, McDropdownMenuPortal, McDropdownMenuTrigger, McDropdownMenuContent, McDropdownMenuGroup, McDropdownMenuLabel, McDropdownMenuItem, McDropdownMenuCheckboxItem, McDropdownMenuRadioGroup, McDropdownMenuRadioItem, McDropdownMenuSeparator, McDropdownMenuShortcut, McDropdownMenuSub, McDropdownMenuSubTrigger, McDropdownMenuSubContent,};Ensure your @/lib/utils helper is correctly imported and your trigger component is available.
Usage
import { Button } from '@/components/ui/button';
import {
McDropdownMenu,
McDropdownMenuContent,
McDropdownMenuItem,
McDropdownMenuTrigger,
} from '@/components/ui/mc-dropdown-menu';
export default function Example() {
return (
<McDropdownMenu>
<McDropdownMenuTrigger render={<Button variant="outline">Open menu</Button>} />
<McDropdownMenuContent>
<McDropdownMenuItem>Profile</McDropdownMenuItem>
<McDropdownMenuItem>Settings</McDropdownMenuItem>
</McDropdownMenuContent>
</McDropdownMenu>
);
}Examples
With Label and Shortcuts
<McDropdownMenu>
<McDropdownMenuTrigger render={<Button variant="outline">Open</Button>} />
<McDropdownMenuContent align="start">
<McDropdownMenuGroup>
<McDropdownMenuLabel>My Account</McDropdownMenuLabel>
<McDropdownMenuItem>
Profile
<McDropdownMenuShortcut>Ctrl+P</McDropdownMenuShortcut>
</McDropdownMenuItem>
<McDropdownMenuItem>
Settings
<McDropdownMenuShortcut>Ctrl+S</McDropdownMenuShortcut>
</McDropdownMenuItem>
</McDropdownMenuGroup>
</McDropdownMenuContent>
</McDropdownMenu>With Submenu
<McDropdownMenu>
<McDropdownMenuTrigger render={<Button variant="outline">Invite</Button>} />
<McDropdownMenuContent>
<McDropdownMenuSub>
<McDropdownMenuSubTrigger>Invite users</McDropdownMenuSubTrigger>
<McDropdownMenuPortal>
<McDropdownMenuSubContent>
<McDropdownMenuItem>Email</McDropdownMenuItem>
<McDropdownMenuItem>Message</McDropdownMenuItem>
<McDropdownMenuItem>Copy link</McDropdownMenuItem>
</McDropdownMenuSubContent>
</McDropdownMenuPortal>
</McDropdownMenuSub>
</McDropdownMenuContent>
</McDropdownMenu>Checkbox Items
<McDropdownMenu>
<McDropdownMenuTrigger render={<Button variant="outline">Preferences</Button>} />
<McDropdownMenuContent align="end">
<McDropdownMenuCheckboxItem checked>Notifications</McDropdownMenuCheckboxItem>
<McDropdownMenuCheckboxItem>Auto updates</McDropdownMenuCheckboxItem>
<McDropdownMenuCheckboxItem disabled>Beta features</McDropdownMenuCheckboxItem>
</McDropdownMenuContent>
</McDropdownMenu>Destructive Action
<McDropdownMenuItem variant="destructive">Delete project</McDropdownMenuItem>API Reference
Root Components
| Component | Description |
|---|---|
McDropdownMenu | Root wrapper that manages the menu state |
McDropdownMenuTrigger | Element that opens the menu |
McDropdownMenuContent | Main popup container for menu items |
McDropdownMenuPortal | Optional portal wrapper for nested menu content |
Structure Components
| Component | Description |
|---|---|
McDropdownMenuGroup | Groups related items together |
McDropdownMenuLabel | Section label inside a menu group |
McDropdownMenuSeparator | Visual divider between groups |
McDropdownMenuShortcut | Shortcut hint aligned to the right |
Item Components
| Component | Description |
|---|---|
McDropdownMenuItem | Standard action item |
McDropdownMenuCheckboxItem | Toggleable checkbox-style item |
McDropdownMenuRadioGroup | Groups radio items into one selectable set |
McDropdownMenuRadioItem | Single radio option |
McDropdownMenuSub | Wrapper for a nested submenu |
McDropdownMenuSubTrigger | Trigger item that opens the submenu |
McDropdownMenuSubContent | Popup content for the submenu |
Common Props
McDropdownMenuContent
| Prop | Type | Default | Description |
|---|---|---|---|
align | "start" | "center" | "end" | "start" | Horizontal alignment relative to the trigger |
alignOffset | number | 0 | Offset applied to the alignment axis |
side | "top" | "right" | "bottom" | "left" | "bottom" | Side where the menu opens |
sideOffset | number | 4 | Offset between the trigger and the popup |
className | string | undefined | Additional classes for the popup |
McDropdownMenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "destructive" | "default" | Visual treatment for the item |
inset | boolean | false | Adds left indentation for aligned content |
disabled | boolean | false | Disables interaction |
className | string | undefined | Additional classes for the item |
McDropdownMenuCheckboxItem and McDropdownMenuRadioItem
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | undefined | Controlled checked state for checkbox items |
inset | boolean | false | Adds left indentation |
disabled | boolean | false | Disables interaction |
className | string | undefined | Additional classes |
All other relevant props from @base-ui/react/menu are supported on their matching wrappers.