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

Loading...
'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:

npx mcoli-ui@latest add mc-dropdown-menu
pnpm dlx mcoli-ui@latest add mc-dropdown-menu
yarn dlx mcoli-ui@latest add mc-dropdown-menu
bunx mcoli-ui@latest add mc-dropdown-menu

Manual Setup

If you prefer manual control:

Install the core dependencies
npm install @base-ui/react lucide-react
pnpm add @base-ui/react lucide-react
yarn add @base-ui/react lucide-react
bun add @base-ui/react lucide-react
Copy the source code
'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,};
Adjust import paths

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

ComponentDescription
McDropdownMenuRoot wrapper that manages the menu state
McDropdownMenuTriggerElement that opens the menu
McDropdownMenuContentMain popup container for menu items
McDropdownMenuPortalOptional portal wrapper for nested menu content

Structure Components

ComponentDescription
McDropdownMenuGroupGroups related items together
McDropdownMenuLabelSection label inside a menu group
McDropdownMenuSeparatorVisual divider between groups
McDropdownMenuShortcutShortcut hint aligned to the right

Item Components

ComponentDescription
McDropdownMenuItemStandard action item
McDropdownMenuCheckboxItemToggleable checkbox-style item
McDropdownMenuRadioGroupGroups radio items into one selectable set
McDropdownMenuRadioItemSingle radio option
McDropdownMenuSubWrapper for a nested submenu
McDropdownMenuSubTriggerTrigger item that opens the submenu
McDropdownMenuSubContentPopup content for the submenu

Common Props

McDropdownMenuContent

PropTypeDefaultDescription
align"start" | "center" | "end""start"Horizontal alignment relative to the trigger
alignOffsetnumber0Offset applied to the alignment axis
side"top" | "right" | "bottom" | "left""bottom"Side where the menu opens
sideOffsetnumber4Offset between the trigger and the popup
classNamestringundefinedAdditional classes for the popup

McDropdownMenuItem

PropTypeDefaultDescription
variant"default" | "destructive""default"Visual treatment for the item
insetbooleanfalseAdds left indentation for aligned content
disabledbooleanfalseDisables interaction
classNamestringundefinedAdditional classes for the item

McDropdownMenuCheckboxItem and McDropdownMenuRadioItem

PropTypeDefaultDescription
checkedbooleanundefinedControlled checked state for checkbox items
insetbooleanfalseAdds left indentation
disabledbooleanfalseDisables interaction
classNamestringundefinedAdditional classes

All other relevant props from @base-ui/react/menu are supported on their matching wrappers.

On this page