# Dropdown Menu

An accessible React dropdown menu for Next.js with a highlight that glides between items, shortcuts, checkbox and radio items and sub-menus, built on Radix.

Source: https://ui.fewtone.com/components/dropdown-menu

## Installation

```bash
npx shadcn@latest add https://ui.fewtone.com/r/dropdown-menu.json
```

## Usage

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

export function AccountMenu() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline">Open menu</Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56" align="start">
        <DropdownMenuLabel>My account</DropdownMenuLabel>
        <DropdownMenuItem>
          Profile
          <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
        </DropdownMenuItem>
        <DropdownMenuItem>Settings</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem variant="destructive">Log out</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## API reference

### DropdownMenu

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | `boolean` | – | Whether the menu is open, when you control it. |
| `defaultOpen` | `boolean` | `false` | Opens the menu at first. |
| `onOpenChange` | `(open: boolean) => void` | – | Called when the menu opens or closes. |
| `modal` | `boolean` | `true` | While open, the rest of the page can't be scrolled or clicked, and screen readers only see the menu. |
| `dir` | `"ltr" \| "rtl"` | – | Reading direction. In rtl, sub-menus open to the left. |

### DropdownMenuTrigger

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Use your own button, for example Button, instead of a plain one. |
| `disabled` | `boolean` | `false` | Prevents opening. |

### DropdownMenuContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `align` | `"start" \| "center" \| "end"` | `"center"` | How it lines up with the trigger. |
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `"bottom"` | The side of the trigger it opens on, when there's room. |
| `sideOffset` | `number` | `4` | Gap between the trigger and the menu, in px. |
| `alignOffset` | `number` | `0` | Shifts it along the trigger, in px. |
| `loop` | `boolean` | `false` | Arrow keys wrap from the last item to the first, and back. |

### DropdownMenuItem

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default" \| "destructive"` | `"default"` | Destructive is red, and the highlight takes a red tint as it lands on it. |
| `inset` | `boolean` | `false` | Indents the label to line up with items that start with an icon. |
| `disabled` | `boolean` | `false` | Skips the item. |
| `onSelect` | `(event: Event) => void` | – | Called when the item is picked with a click, Enter or Space. The menu then closes; call event.preventDefault() to keep it open. |
| `textValue` | `string` | – | The text typeahead matches, when the item's content isn't plain text. |

### DropdownMenuCheckboxItem

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `checked` | `boolean \| "indeterminate"` | `false` | Whether it's on. Indeterminate draws a dash. |
| `onCheckedChange` | `(checked: boolean) => void` | – | Called with the new state when it's picked. |
| `disabled` | `boolean` | `false` | Skips the item. |
| `onSelect` | `(event: Event) => void` | – | Call event.preventDefault() to keep the menu open while switching. |

### DropdownMenuRadioGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | – | The picked item's value. |
| `onValueChange` | `(value: string) => void` | – | Called with the new value when another item is picked. |

### DropdownMenuRadioItem

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | – | The value this item picks. |
| `disabled` | `boolean` | `false` | Skips the item. |
| `onSelect` | `(event: Event) => void` | – | Call event.preventDefault() to keep the menu open while choosing. |

### DropdownMenuLabel

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `inset` | `boolean` | `false` | Indents it to line up with items that start with an icon. |

### DropdownMenuGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | – | The items, often after a DropdownMenuLabel. |

### DropdownMenuSeparator

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | – | Extra classes, merged with cn. |

### DropdownMenuShortcut

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | – | The keys, for example ⌘K. It only shows them: bind the keys yourself. |

### DropdownMenuSub

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | `boolean` | – | Whether the sub-menu is open, when you control it. |
| `defaultOpen` | `boolean` | `false` | Opens it at first. |
| `onOpenChange` | `(open: boolean) => void` | – | Called when it opens or closes. |

### DropdownMenuSubTrigger

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `inset` | `boolean` | `false` | Indents the label to line up with items that start with an icon. |
| `disabled` | `boolean` | `false` | Skips the item. |

### DropdownMenuSubContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `sideOffset` | `number` | `4` | Gap between the menu and the sub-menu, in px. |
| `alignOffset` | `number` | `-5` | Shifts it along the trigger, in px. The default lines its first item up with the trigger. |
| `loop` | `boolean` | `false` | Arrow keys wrap from the last item to the first, and back. |

### DropdownMenuPortal

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `container` | `HTMLElement \| null` | `document.body` | Where to render instead. |

**When to use it:** Use DropdownMenu for actions or settings behind a button: an account menu, a row's More actions, view options. Use Select to pick a form value, ContextMenu for the same actions on right-click, and a popover for rich content or forms.
**Built on:** Radix Dropdown Menu.
**Accessibility pattern:** https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/
**npm packages (installed by the CLI):** class-variance-authority@^0.7.1, lucide-react@^1.52.0, motion@^14.0.0, radix-ui@^1.7.0.

## Examples

### Account menu

Items with shortcuts, a setting to switch, a sub-menu and a destructive Log out. Hover or use the arrow keys: one highlight glides between the items, and fades out when the pointer leaves.

```tsx
"use client";

import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useState } from "react";

/** An account menu: items with shortcuts, a setting to switch, a sub-menu and a destructive action */
export default function DropdownMenuAccount() {
  const [notifications, setNotifications] = useState(true);

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline">Open menu</Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56" align="start">
        <DropdownMenuLabel>My account</DropdownMenuLabel>
        <DropdownMenuGroup>
          <DropdownMenuItem>
            Profile
            <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            Billing
            <DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            Settings
            <DropdownMenuShortcut>⌘,</DropdownMenuShortcut>
          </DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          {/* Switching a setting keeps the menu open, so you see the check draw itself in */}
          <DropdownMenuCheckboxItem
            checked={notifications}
            onCheckedChange={setNotifications}
            onSelect={(event) => event.preventDefault()}
          >
            Email notifications
          </DropdownMenuCheckboxItem>
          <DropdownMenuSub>
            <DropdownMenuSubTrigger>Invite people</DropdownMenuSubTrigger>
            <DropdownMenuSubContent className="w-44">
              <DropdownMenuItem>Email</DropdownMenuItem>
              <DropdownMenuItem>Message</DropdownMenuItem>
              <DropdownMenuSeparator />
              <DropdownMenuItem>Copy invite link</DropdownMenuItem>
            </DropdownMenuSubContent>
          </DropdownMenuSub>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuItem variant="destructive">
          Log out
          <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
```

### Checkbox and radio items

Checkbox items draw their check in and erase it backwards; radio items grow their dot. This menu stays open while you choose: call event.preventDefault() in onSelect.

```tsx
"use client";

import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuLabel,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useState } from "react";

const panels = [
  { value: "top", label: "Top" },
  { value: "bottom", label: "Bottom" },
  { value: "right", label: "Right" },
];

/** View options: settings switched on and off, and one choice from a set. The menu stays open while you choose. */
export default function DropdownMenuOptions() {
  const [statusBar, setStatusBar] = useState(true);
  const [minimap, setMinimap] = useState(false);
  const [panel, setPanel] = useState("bottom");
  const keepOpen = (event: Event) => event.preventDefault();

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline">View</Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-52">
        <DropdownMenuLabel>Show</DropdownMenuLabel>
        <DropdownMenuCheckboxItem checked={statusBar} onCheckedChange={setStatusBar} onSelect={keepOpen}>
          Status bar
        </DropdownMenuCheckboxItem>
        <DropdownMenuCheckboxItem checked={minimap} onCheckedChange={setMinimap} onSelect={keepOpen}>
          Minimap
        </DropdownMenuCheckboxItem>
        <DropdownMenuCheckboxItem disabled>Activity bar</DropdownMenuCheckboxItem>
        <DropdownMenuSeparator />
        <DropdownMenuLabel>Panel position</DropdownMenuLabel>
        <DropdownMenuRadioGroup value={panel} onValueChange={setPanel}>
          {panels.map(({ value, label }) => (
            <DropdownMenuRadioItem key={value} value={value} onSelect={keepOpen}>
              {label}
            </DropdownMenuRadioItem>
          ))}
        </DropdownMenuRadioGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
```

### Icon trigger and icons

An icon button opens a row's actions, and items can start with an icon. The highlight takes a red tint on a destructive item.

```tsx
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { ArchiveIcon, CopyIcon, EllipsisIcon, PencilIcon, Trash2Icon } from "lucide-react";

/** A row's actions behind an icon button, each item led by an icon */
export default function DropdownMenuIcons() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="icon" aria-label="More actions">
          <EllipsisIcon />
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-48" align="end">
        <DropdownMenuItem>
          <PencilIcon aria-hidden />
          Edit
          <DropdownMenuShortcut>⌘E</DropdownMenuShortcut>
        </DropdownMenuItem>
        <DropdownMenuItem>
          <CopyIcon aria-hidden />
          Duplicate
          <DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
        </DropdownMenuItem>
        <DropdownMenuItem>
          <ArchiveIcon aria-hidden />
          Archive
        </DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem variant="destructive">
          <Trash2Icon aria-hidden />
          Delete
          <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
```

## Related

- [Context Menu](https://ui.fewtone.com/components/context-menu.md): An accessible React context menu for Next.js: a right-click menu with a gliding highlight, shortcuts, checkbox and radio items and sub-menus, built on Radix.
- [Select](https://ui.fewtone.com/components/select.md): An accessible React select with a gliding highlight, groups, labels and a scrollable list, built on Radix Select.
- [Button](https://ui.fewtone.com/components/button.md): An accessible React button for Next.js and Tailwind CSS with six variants, three sizes, icons and a soft tint on hover and press, built on a native button.
