ComponentsDropdownMenu
DropdownMenu
Show a menu of actions when users open a trigger.
On this page
Use this component from the local workspace package. Follow the setup guide first. A public npm package and registry are not available yet.
View registry filesImport the component into your page or component file.
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemLabel,
DropdownMenuSection,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@dethink/components";Try the examples, then open the code to use them in your app. Open a menu and navigate with arrows, Home/End, or by typing an item name.
Sections, icons, and shortcuts
A grouped menu with a label, icon and shortcut parts, a separator, and a destructive item.
Show sourceexamples/dropdown-menu/basic.tsx
"use client";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemIcon,
DropdownMenuItemLabel,
DropdownMenuItemShortcut,
DropdownMenuLabel,
DropdownMenuSection,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@dethink/components";
import { Copy, ExternalLink, RefreshCw, Trash2 } from "lucide-react";
export function DropdownMenuBasic() {
return (
<div className="flex justify-center">
<DropdownMenu>
<DropdownMenuTrigger variant="outline">
Report actions
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuSection>
<DropdownMenuLabel>Report</DropdownMenuLabel>
<DropdownMenuItem>
<DropdownMenuItemIcon>
<ExternalLink aria-hidden="true" />
</DropdownMenuItemIcon>
<DropdownMenuItemLabel>Open report</DropdownMenuItemLabel>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownMenuItemIcon>
<RefreshCw aria-hidden="true" />
</DropdownMenuItemIcon>
<DropdownMenuItemLabel>Refresh data</DropdownMenuItemLabel>
<DropdownMenuItemShortcut>R</DropdownMenuItemShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownMenuItemIcon>
<Copy aria-hidden="true" />
</DropdownMenuItemIcon>
<DropdownMenuItemLabel>Duplicate</DropdownMenuItemLabel>
<DropdownMenuItemShortcut>⌘D</DropdownMenuItemShortcut>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem destructive>
<DropdownMenuItemIcon>
<Trash2 aria-hidden="true" />
</DropdownMenuItemIcon>
<DropdownMenuItemLabel>Delete report</DropdownMenuItemLabel>
</DropdownMenuItem>
</DropdownMenuSection>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}Submenu and descriptions
DropdownMenuSubmenu nests a flyout; item descriptions add secondary context; disabled items stay visible but inert.
Examples that combine components for common tasks.
Row actions
One icon trigger per row, each labelled with the row's name for screen readers. onAction mutates the live list, the destructive item is styled as such, and a live region reports what happened.
production-api
Last used 2 minutes ago
staging-api
Last used 3 days ago
ci-deploy
Last used just now
Show sourceexamples/dropdown-menu/recipe-row-actions.tsx
"use client";
import { useState } from "react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemLabel,
DropdownMenuSection,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@dethink/components";
import { MoreHorizontal } from "lucide-react";
const initialKeys = [
{ id: "k1", name: "production-api", lastUsed: "2 minutes ago" },
{ id: "k2", name: "staging-api", lastUsed: "3 days ago" },
{ id: "k3", name: "ci-deploy", lastUsed: "just now" },
];
/**
* The menu's most common seat: one icon trigger per row, labelled with the
* row's name so screen-reader users know which record the actions target.
* Destructive items get the destructive treatment, and actions mutate the
* live list.
*/
export function DropdownMenuRecipeRowActions() {
const [keys, setKeys] = useState(initialKeys);
const [status, setStatus] = useState("");
return (
<div className="mx-auto max-w-sm space-y-2">
<ul className="divide-border border-border divide-y rounded-lg border">
{keys.map((apiKey) => (
<li
key={apiKey.id}
className="flex items-center justify-between gap-3 px-4 py-2.5"
>
<div>
<p className="font-mono text-sm">{apiKey.name}</p>
<p className="text-muted-foreground text-xs">
Last used {apiKey.lastUsed}
</p>
</div>
<DropdownMenu>
<DropdownMenuTrigger
variant="ghost"
size="icon"
aria-label={`Actions for ${apiKey.name}`}
>
<MoreHorizontal className="size-4" aria-hidden="true" />
</DropdownMenuTrigger>
<DropdownMenuContent placement="bottom end">
<DropdownMenuSection>
<DropdownMenuItem
onAction={() => setStatus(`Copied ${apiKey.name}.`)}
>
<DropdownMenuItemLabel>Copy key</DropdownMenuItemLabel>
</DropdownMenuItem>
<DropdownMenuItem
onAction={() => setStatus(`Rotated ${apiKey.name}.`)}
>
<DropdownMenuItemLabel>Rotate…</DropdownMenuItemLabel>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
destructive
onAction={() => {
setKeys((current) =>
current.filter((entry) => entry.id !== apiKey.id),
);
setStatus(`Revoked ${apiKey.name}.`);
}}
>
<DropdownMenuItemLabel>Revoke</DropdownMenuItemLabel>
</DropdownMenuItem>
</DropdownMenuSection>
</DropdownMenuContent>
</DropdownMenu>
</li>
))}
</ul>
<p aria-live="polite" className="text-muted-foreground text-sm">
{status}
</p>
</div>
);
}DropdownMenu coordinates trigger and content; items carry actions and states.
| Prop | What it does | Default |
|---|---|---|
open / defaultOpen / onOpenChangeboolean / boolean / (open) => void | Controlled or uncontrolled menu visibility. | Not set |
motionPreset / reducedMotion"none" | "subtle" | "standard" / boolean | Motion owns surface presence and changed item feedback. Reduced motion removes transforms while preserving immediate state styling. | "standard" / user preference |
DropdownMenuTriggervariant + size (Button API) | The menu button; give icon-only triggers an aria-label naming their target. | Not set |
DropdownMenuContent — placement"bottom" | "top" | … (+ " start"/" end") | Preferred side and alignment relative to the trigger. | "bottom" |
DropdownMenuItem — onAction() => void | Runs when the item is selected by click or keyboard. | Not set |
DropdownMenuItem — destructive / disabledboolean | Destructive styling with data-destructive, or a disabled non-interactive item. | false |
Item anatomyIcon | Label | Description | Shortcut | DropdownMenuItemIcon/Label/Description/Shortcut compose rich rows inside an item. | Not set |
StructureSection | Label | Separator | Submenu | DropdownMenuSection groups, DropdownMenuLabel titles a group, DropdownMenuSeparator divides, and DropdownMenuSubmenu + SubmenuContent nest a flyout. | Not set |
DropdownMenu now has one animation owner.
Surface entry and exit presence plus changed item feedback use primitives from motion/react. Registry installs now declare Motion as a runtime dependency. Tailwind overlay keyframes and transition utilities no longer run on the DropdownMenu path; the reducedMotion prop or user preference removes transform choreography without changing roles, focus, placement, portals, or submenu behavior.