# Dropdown Menu

Displays a menu of actions or functions, opened from a button.

```tsx
import * as stylex from "@stylexjs/stylex"
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const styles = stylex.create({
  menu: { minWidth: "10rem" },
})

export default function DropdownMenuDemo() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>Open</DropdownMenuTrigger>
      <DropdownMenuContent xstyle={styles.menu}>
        <DropdownMenuGroup>
          <DropdownMenuLabel>My Account</DropdownMenuLabel>
          <DropdownMenuItem>
            Profile
            <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            Billing
            <DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            Settings
            <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
          </DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          <DropdownMenuItem>Team</DropdownMenuItem>
          <DropdownMenuSub>
            <DropdownMenuSubTrigger>Invite users</DropdownMenuSubTrigger>
            <DropdownMenuSubContent>
              <DropdownMenuItem>Email</DropdownMenuItem>
              <DropdownMenuItem>Message</DropdownMenuItem>
              <DropdownMenuSeparator />
              <DropdownMenuItem>More...</DropdownMenuItem>
            </DropdownMenuSubContent>
          </DropdownMenuSub>
          <DropdownMenuItem>
            New Team
            <DropdownMenuShortcut>⌘+T</DropdownMenuShortcut>
          </DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          <DropdownMenuItem>GitHub</DropdownMenuItem>
          <DropdownMenuItem>Support</DropdownMenuItem>
          <DropdownMenuItem disabled>API</DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuItem>
          Log out
          <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## Installation

1. Install the following dependencies:

   ```bash
   npm install @base-ui/react @stylexjs/stylex clsx lucide-react
   ```

2. Set up the token layer and the StyleX compiler, if you have not already. See
   [Installation](https://carbon.style/docs/installation), and paste `states.ts` and `state-styles.ts`
   from the same page. `states.ts` holds the popup surface and the highlighted and
   dimmed item styles. The items and the submenu trigger read their Base UI state
   through `state-styles.ts`. Include `interop.css` for item icon sizing and the open
   background on button triggers.

3. Copy and paste the following code into your project.

   ```tsx title="components/ui/dropdown-menu.tsx"
   "use client"

   import { useRender } from "@base-ui/react/use-render"
   import type { CSSProperties } from "react"
   import { Menu as MenuPrimitive } from "@base-ui/react/menu"
   import * as stylex from "@stylexjs/stylex"
   import type { StyleXStyles } from "@stylexjs/stylex"
   import { clsx } from "clsx"
   import { Check, ChevronRight } from "lucide-react"

   import { stateStyles } from "@/lib/state-styles"
   import { states } from "@/theme/states"
   import {
     color,
     duration,
     ease,
     layer,
     leading,
     radius,
     shadow,
     space,
     text,
     tracking,
   } from "@/theme/tokens.stylex"

   const styles = stylex.create({
     hidden: { opacity: 0 },
     content: {
       zIndex: layer.popup,
       maxHeight: "var(--available-height)",
       minWidth: "max(8rem, var(--anchor-width))",
       overflowX: "hidden",
       overflowY: "auto",
       "--carbon-popup-radius": radius.lg,
       borderRadius: "var(--carbon-popup-radius)",
       padding: space.s1,
       transformOrigin: "var(--transform-origin)",
       transitionProperty: "opacity, transform",
       transitionDuration: {
         default: duration.fade,
         "@media (prefers-reduced-motion: reduce)": "0s",
       },
       transitionTimingFunction: ease.standard,
     },
     subContent: {
       width: "auto",
       minWidth: "6rem",
       boxShadow: shadow.popupLifted,
     },
     label: {
       paddingInline: space.s1_5,
       paddingBlock: space.s1,
       fontSize: text.xs,
       lineHeight: leading.xs,
       fontWeight: 500,
       color: color.mutedForeground,
     },
     item: {
       position: "relative",
       display: "flex",
       alignItems: "center",
       gap: space.s1_5,
       cursor: "default",
       userSelect: "none",
       paddingInline: space.s1_5,
       paddingBlock: space.s1,
       fontSize: text.sm,
       lineHeight: leading.sm,
       outlineStyle: "none",
       "--carbon-icon-size": space.s4,
     },
     inset: { paddingInlineStart: space.s7 },
     destructive: { color: color.destructive },
     indicatorItem: { paddingInlineEnd: space.s8 },
     indicator: {
       pointerEvents: "none",
       position: "absolute",
       insetInlineEnd: space.s2,
       display: "flex",
       alignItems: "center",
       justifyContent: "center",
     },
     chevron: { marginInlineStart: "auto" },
     separator: {
       marginInline: `calc(-1 * ${space.s1})`,
       marginBlock: space.s1,
       height: 1,
       backgroundColor: color.border,
     },
     shortcut: {
       marginInlineStart: "auto",
       fontSize: text.xs,
       lineHeight: leading.xs,
       letterSpacing: tracking.widest,
       color: color.mutedForeground,
     },
   })

   type ItemVariant = "default" | "destructive"

   const slide = stylex.create({
     top: { transform: `scale(0.95) translateY(${space.s2})` },
     bottom: { transform: `scale(0.95) translateY(calc(-1 * ${space.s2}))` },
     left: { transform: `scale(0.95) translateX(${space.s2})` },
     right: { transform: `scale(0.95) translateX(calc(-1 * ${space.s2}))` },
     "inline-start": { transform: `scale(0.95) translateX(${space.s2})` },
     "inline-end": { transform: `scale(0.95) translateX(calc(-1 * ${space.s2}))` },
   })

   type Overrides = { xstyle?: StyleXStyles; className?: string }

   type StateOverrides = Overrides & { style?: CSSProperties }

   function DropdownMenu(props: MenuPrimitive.Root.Props) {
     return <MenuPrimitive.Root {...props} />
   }

   function DropdownMenuPortal(props: MenuPrimitive.Portal.Props) {
     return <MenuPrimitive.Portal {...props} />
   }

   function DropdownMenuTrigger(props: MenuPrimitive.Trigger.Props) {
     return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
   }

   type DropdownMenuContentProps = Omit<MenuPrimitive.Popup.Props, "className" | "style"> &
     Pick<MenuPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset"> &
     StateOverrides

   function DropdownMenuContent({
     xstyle,
     style,
     className,
     side = "bottom",
     sideOffset = 4,
     align = "start",
     alignOffset = 0,
     ...props
   }: DropdownMenuContentProps) {
     return (
       <MenuPrimitive.Portal>
         <MenuPrimitive.Positioner
           align={align}
           alignOffset={alignOffset}
           side={side}
           sideOffset={sideOffset}
           {...stylex.props(states.positioner)}
         >
           <MenuPrimitive.Popup
             data-slot="dropdown-menu-content"
             {...props}
             {...stateStyles(
               (state) => [
                 states.popupSurface,
                 styles.content,
                 (state.transitionStatus === "starting" || state.transitionStatus === "ending") &&
                   styles.hidden,
                 (state.transitionStatus === "starting" || state.transitionStatus === "ending") &&
                   slide[state.side],
                 xstyle,
               ],
               { className, style },
             )}
           />
         </MenuPrimitive.Positioner>
       </MenuPrimitive.Portal>
     )
   }

   function DropdownMenuGroup(props: MenuPrimitive.Group.Props) {
     return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
   }

   type DropdownMenuLabelProps = Omit<MenuPrimitive.GroupLabel.Props, "className"> &
     Overrides & { inset?: boolean }

   function DropdownMenuLabel({ xstyle, className, inset, ...props }: DropdownMenuLabelProps) {
     const sx = stylex.props(styles.label, inset && styles.inset, xstyle)
     return (
       <MenuPrimitive.GroupLabel
         data-slot="dropdown-menu-label"
         data-inset={inset || undefined}
         {...sx}
         {...props}
         className={clsx(sx.className, className)}
       />
     )
   }

   type DropdownMenuItemProps = Omit<MenuPrimitive.Item.Props, "className" | "style"> &
     StateOverrides & { inset?: boolean; variant?: ItemVariant }

   function DropdownMenuItem({
     xstyle,
     style,
     className,
     inset,
     variant = "default",
     ...props
   }: DropdownMenuItemProps) {
     const destructive = variant === "destructive"
     return (
       <MenuPrimitive.Item
         data-slot="dropdown-menu-item"
         data-inset={inset || undefined}
         data-variant={variant}
         {...props}
         {...stateStyles(
           (state) => [
             styles.item,
             states.popupItem,
             inset && styles.inset,
             destructive && styles.destructive,
             state.highlighted &&
               (destructive ? states.itemHighlightDestructive : states.itemHighlight),
             state.disabled && states.disabled,
             xstyle,
           ],
           { className, style, hook: "carbon-icons" },
         )}
       />
     )
   }

   type DropdownMenuLinkItemProps = Omit<MenuPrimitive.LinkItem.Props, "className" | "style"> &
     StateOverrides & { inset?: boolean }

   function DropdownMenuLinkItem({
     xstyle,
     style,
     className,
     inset,
     ...props
   }: DropdownMenuLinkItemProps) {
     return (
       <MenuPrimitive.LinkItem
         data-slot="dropdown-menu-link-item"
         data-inset={inset || undefined}
         {...props}
         {...stateStyles(
           (state) => [
             styles.item,
             states.popupItem,
             inset && styles.inset,
             state.highlighted && states.itemHighlight,
             xstyle,
           ],
           { className, style, hook: "carbon-icons" },
         )}
       />
     )
   }

   type DropdownMenuCheckboxItemProps = Omit<MenuPrimitive.CheckboxItem.Props, "className" | "style"> &
     StateOverrides & { inset?: boolean }

   function DropdownMenuCheckboxItem({
     xstyle,
     style,
     className,
     inset,
     children,
     ...props
   }: DropdownMenuCheckboxItemProps) {
     return (
       <MenuPrimitive.CheckboxItem
         data-slot="dropdown-menu-checkbox-item"
         data-inset={inset || undefined}
         {...props}
         {...stateStyles(
           (state) => [
             styles.item,
             states.popupItem,
             styles.indicatorItem,
             inset && styles.inset,
             state.highlighted && states.itemHighlight,
             state.disabled && states.disabled,
             xstyle,
           ],
           { className, style, hook: "carbon-icons" },
         )}
       >
         <span data-slot="dropdown-menu-checkbox-item-indicator" {...stylex.props(styles.indicator)}>
           <MenuPrimitive.CheckboxItemIndicator>
             <Check />
           </MenuPrimitive.CheckboxItemIndicator>
         </span>
         {children}
       </MenuPrimitive.CheckboxItem>
     )
   }

   function DropdownMenuRadioGroup(props: MenuPrimitive.RadioGroup.Props) {
     return <MenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />
   }

   type DropdownMenuRadioItemProps = Omit<MenuPrimitive.RadioItem.Props, "className" | "style"> &
     StateOverrides & { inset?: boolean }

   function DropdownMenuRadioItem({
     xstyle,
     style,
     className,
     inset,
     children,
     ...props
   }: DropdownMenuRadioItemProps) {
     return (
       <MenuPrimitive.RadioItem
         data-slot="dropdown-menu-radio-item"
         data-inset={inset || undefined}
         {...props}
         {...stateStyles(
           (state) => [
             styles.item,
             states.popupItem,
             styles.indicatorItem,
             inset && styles.inset,
             state.highlighted && states.itemHighlight,
             state.disabled && states.disabled,
             xstyle,
           ],
           { className, style, hook: "carbon-icons" },
         )}
       >
         <span data-slot="dropdown-menu-radio-item-indicator" {...stylex.props(styles.indicator)}>
           <MenuPrimitive.RadioItemIndicator>
             <Check />
           </MenuPrimitive.RadioItemIndicator>
         </span>
         {children}
       </MenuPrimitive.RadioItem>
     )
   }

   type DropdownMenuSeparatorProps = Omit<MenuPrimitive.Separator.Props, "className"> & Overrides

   function DropdownMenuSeparator({ xstyle, className, ...props }: DropdownMenuSeparatorProps) {
     const sx = stylex.props(styles.separator, xstyle)
     return (
       <MenuPrimitive.Separator
         data-slot="dropdown-menu-separator"
         {...sx}
         {...props}
         className={clsx(sx.className, className)}
       />
     )
   }

   type DropdownMenuShortcutProps = useRender.ComponentProps<"span"> & Overrides

   function DropdownMenuShortcut({
     render,
     ref,
     style,
     xstyle,
     className,
     ...props
   }: DropdownMenuShortcutProps) {
     const sx = stylex.props(styles.shortcut, xstyle)
     return useRender({
       defaultTagName: "span",
       render,
       ref,
       props: {
         "data-slot": "dropdown-menu-shortcut",
         ...sx,
         ...props,
         className: clsx(sx.className, className),
         style: { ...sx.style, ...style },
       },
     })
   }

   function DropdownMenuSub(props: MenuPrimitive.SubmenuRoot.Props) {
     return <MenuPrimitive.SubmenuRoot {...props} />
   }

   type DropdownMenuSubTriggerProps = Omit<MenuPrimitive.SubmenuTrigger.Props, "className" | "style"> &
     StateOverrides & { inset?: boolean }

   function DropdownMenuSubTrigger({
     xstyle,
     style,
     className,
     inset,
     children,
     ...props
   }: DropdownMenuSubTriggerProps) {
     return (
       <MenuPrimitive.SubmenuTrigger
         data-slot="dropdown-menu-sub-trigger"
         data-inset={inset || undefined}
         {...props}
         {...stateStyles(
           (state) => [
             styles.item,
             states.popupItem,
             inset && styles.inset,
             (state.highlighted || state.open) && states.itemHighlight,
             state.disabled && states.disabled,
             xstyle,
           ],
           { className, style, hook: "carbon-icons" },
         )}
       >
         {children}
         <ChevronRight {...stylex.props(styles.chevron)} />
       </MenuPrimitive.SubmenuTrigger>
     )
   }

   function DropdownMenuSubContent({
     xstyle,
     side = "right",
     sideOffset = 0,
     align = "start",
     alignOffset = -3,
     ...props
   }: DropdownMenuContentProps) {
     return (
       <DropdownMenuContent
         data-slot="dropdown-menu-sub-content"
         side={side}
         sideOffset={sideOffset}
         align={align}
         alignOffset={alignOffset}
         xstyle={[styles.subContent, xstyle]}
         {...props}
       />
     )
   }

   export {
     DropdownMenu,
     DropdownMenuCheckboxItem,
     DropdownMenuContent,
     DropdownMenuGroup,
     DropdownMenuItem,
     DropdownMenuLabel,
     DropdownMenuLinkItem,
     DropdownMenuPortal,
     DropdownMenuRadioGroup,
     DropdownMenuRadioItem,
     DropdownMenuSeparator,
     DropdownMenuShortcut,
     DropdownMenuSub,
     DropdownMenuSubContent,
     DropdownMenuSubTrigger,
     DropdownMenuTrigger,
   }
   ```

4. Update the import paths to match your project setup.

## Usage

```tsx
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
```

```tsx
<DropdownMenu>
  <DropdownMenuTrigger render={<Button variant="outline" />}>Open</DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuGroup>
      <DropdownMenuLabel>My Account</DropdownMenuLabel>
      <DropdownMenuItem>Profile</DropdownMenuItem>
      <DropdownMenuItem>Billing</DropdownMenuItem>
    </DropdownMenuGroup>
    <DropdownMenuSeparator />
    <DropdownMenuItem>Log out</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>
```

Pass the trigger element through `render`. Base UI merges `aria-haspopup` and
`aria-expanded` into it and the `Button` keeps its styles.

## Basic

Use `DropdownMenuLabel` to name a group and `DropdownMenuSeparator` to divide
groups.

```tsx
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export default function DropdownMenuBasic() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>Open</DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuGroup>
          <DropdownMenuLabel>My Account</DropdownMenuLabel>
          <DropdownMenuItem>Profile</DropdownMenuItem>
          <DropdownMenuItem>Billing</DropdownMenuItem>
          <DropdownMenuItem>Settings</DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuItem>GitHub</DropdownMenuItem>
        <DropdownMenuItem>Support</DropdownMenuItem>
        <DropdownMenuItem disabled>API</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## Submenu

Nest secondary actions in a `DropdownMenuSub`.

```tsx
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export default function DropdownMenuSubmenu() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>Open</DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem>Team</DropdownMenuItem>
        <DropdownMenuSub>
          <DropdownMenuSubTrigger>Invite users</DropdownMenuSubTrigger>
          <DropdownMenuSubContent>
            <DropdownMenuItem>Email</DropdownMenuItem>
            <DropdownMenuItem>Message</DropdownMenuItem>
            <DropdownMenuSub>
              <DropdownMenuSubTrigger>More options</DropdownMenuSubTrigger>
              <DropdownMenuSubContent>
                <DropdownMenuItem>Calendly</DropdownMenuItem>
                <DropdownMenuItem>Slack</DropdownMenuItem>
                <DropdownMenuSeparator />
                <DropdownMenuItem>Webhook</DropdownMenuItem>
              </DropdownMenuSubContent>
            </DropdownMenuSub>
            <DropdownMenuSeparator />
            <DropdownMenuItem>Advanced...</DropdownMenuItem>
          </DropdownMenuSubContent>
        </DropdownMenuSub>
        <DropdownMenuItem>New Team</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## Shortcuts

Use `DropdownMenuShortcut` to show a key combination at the trailing edge of an
item. It displays the key only. Wire the binding yourself.

```tsx
import * as stylex from "@stylexjs/stylex"
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const styles = stylex.create({
  menu: { minWidth: "10rem" },
})

export default function DropdownMenuShortcuts() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>Open</DropdownMenuTrigger>
      <DropdownMenuContent xstyle={styles.menu}>
        <DropdownMenuGroup>
          <DropdownMenuLabel>My Account</DropdownMenuLabel>
          <DropdownMenuItem>
            Profile
            <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            Billing
            <DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            Settings
            <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
          </DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuItem>
          Log out
          <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## Icons

Place an icon before the label. The item sizes it.

```tsx
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { CreditCard, LogOut, Settings, User } from "lucide-react"

export default function DropdownMenuIcons() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>Open</DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem>
          <User />
          Profile
        </DropdownMenuItem>
        <DropdownMenuItem>
          <CreditCard />
          Billing
        </DropdownMenuItem>
        <DropdownMenuItem>
          <Settings />
          Settings
        </DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem variant="destructive">
          <LogOut />
          Log out
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## Checkboxes

Use `DropdownMenuCheckboxItem` for items that toggle. Pass `checked` and
`onCheckedChange` to control it, or `defaultChecked` to leave it uncontrolled.

```tsx
import * as stylex from "@stylexjs/stylex"
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuLabel,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const styles = stylex.create({
  menu: { minWidth: "10rem" },
})

export default function DropdownMenuCheckboxes() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>Open</DropdownMenuTrigger>
      <DropdownMenuContent xstyle={styles.menu}>
        <DropdownMenuGroup>
          <DropdownMenuLabel>Appearance</DropdownMenuLabel>
          <DropdownMenuCheckboxItem defaultChecked>Status Bar</DropdownMenuCheckboxItem>
          <DropdownMenuCheckboxItem disabled>Activity Bar</DropdownMenuCheckboxItem>
          <DropdownMenuCheckboxItem>Panel</DropdownMenuCheckboxItem>
        </DropdownMenuGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## Radio group

Use `DropdownMenuRadioGroup` to hold one value across its
`DropdownMenuRadioItem`s. Pass `value` and `onValueChange`, or `defaultValue`.

```tsx
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuLabel,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export default function DropdownMenuRadioGroupDemo() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>Open</DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuGroup>
          <DropdownMenuLabel>Panel Position</DropdownMenuLabel>
          <DropdownMenuRadioGroup defaultValue="bottom">
            <DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
            <DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
            <DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
          </DropdownMenuRadioGroup>
        </DropdownMenuGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## Destructive

Set `variant="destructive"` on an item to colour it and its icon.

```tsx
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Pencil, Share, Trash } from "lucide-react"

export default function DropdownMenuDestructive() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>Actions</DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuGroup>
          <DropdownMenuItem>
            <Pencil />
            Edit
          </DropdownMenuItem>
          <DropdownMenuItem>
            <Share />
            Share
          </DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuItem variant="destructive">
          <Trash />
          Delete
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## API reference

Pass `render` to replace a part's element or compose it with another component.

Every part wraps the Base UI `Menu` part of the same name and accepts its
props. See the [Base UI reference](https://base-ui.com/react/components/menu)
for the full list. Each styled part also takes `xstyle` and `className`. The
items and `DropdownMenuSubTrigger` also take `style`, which is real inline CSS
merged over the style their Base UI state resolves to.

### DropdownMenuContent

| Prop          | Type                                                                       | Default    |
| ------------- | -------------------------------------------------------------------------- | ---------- |
| `side`        | `"top" \| "bottom" \| "left" \| "right" \| "inline-start" \| "inline-end"` | `"bottom"` |
| `sideOffset`  | `number`                                                                   | `4`        |
| `align`       | `"start" \| "center" \| "end"`                                             | `"start"`  |
| `alignOffset` | `number`                                                                   | `0`        |

The popup is at least as wide as its trigger and never narrower than `8rem`.
Set a wider floor through `xstyle`.

### DropdownMenuSubContent

The same props, with `side="right"`, `sideOffset={0}` and `alignOffset={-3}`
as defaults.

### DropdownMenuItem

| Prop       | Type                         | Default     |
| ---------- | ---------------------------- | ----------- |
| `variant`  | `"default" \| "destructive"` | `"default"` |
| `inset`    | `boolean`                    | —           |
| `disabled` | `boolean`                    | —           |

`inset` pads the leading edge by the width of an indicator column, so a plain
item lines up with checkbox and radio items beside it. `DropdownMenuLabel`,
`DropdownMenuSubTrigger`, `DropdownMenuCheckboxItem` and `DropdownMenuRadioItem`
take `inset` too.

### DropdownMenuLinkItem

An item that navigates. It wraps Base UI's `Menu.LinkItem` and renders an `<a>`,
styled like `DropdownMenuItem`. Use it for links instead of giving an item
`render={<a />}`.

| Prop           | Type      | Default |
| -------------- | --------- | ------- |
| `href`         | `string`  | —       |
| `closeOnClick` | `boolean` | `false` |
| `inset`        | `boolean` | —       |

The menu stays open on click, since a navigation usually unmounts it. Set
`closeOnClick` on a link that opens a new tab or another app.

### DropdownMenuCheckboxItem

| Prop              | Type                         | Default |
| ----------------- | ---------------------------- | ------- |
| `checked`         | `boolean`                    | —       |
| `defaultChecked`  | `boolean`                    | —       |
| `onCheckedChange` | `(checked: boolean) => void` | —       |

### DropdownMenuRadioGroup

| Prop            | Type                      | Default |
| --------------- | ------------------------- | ------- |
| `value`         | `string`                  | —       |
| `defaultValue`  | `string`                  | —       |
| `onValueChange` | `(value: string) => void` | —       |

### Data attributes

| Attribute                                  | On        | Effect                                                     |
| ------------------------------------------ | --------- | ---------------------------------------------------------- |
| `data-slot="dropdown-menu-content"`        | the popup | Popup surface                                              |
| `data-side`, `data-align`                  | the popup | Base UI writes them. `data-side` picks the slide direction |
| `data-starting-style`, `data-ending-style` | the popup | Base UI writes them while the popup animates in or out     |
| `data-slot="dropdown-menu-item"`           | an item   | Targets items from a parent                                |
| `data-slot="dropdown-menu-link-item"`      | a link    | Targets link items from a parent                           |
| `data-variant`                             | an item   | Reflects the `variant` prop                                |
| `data-inset`                               | an item   | Present when `inset` is set                                |
| `data-highlighted`, `data-disabled`        | an item   | Base UI writes them                                        |
