Dropdown Menu
A contextual popover menu presenting a list of actions or shortcuts, featuring radio item groups, checkable item groups, search input filters, custom arrow pointers, and submenus.
import
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent } from "@/components/ui/dropdownMenu/dropdownMenu";$
npx @bloomui-react/cli add dropdownMenudropdownMenu.tsx
"use client";
import { Icon } from "@iconify/react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import * as React from "react";
import { cn } from "@/lib/utils";
const DropdownMenu = ({
modal = false,
...props
}: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root>) => (
<DropdownMenuPrimitive.Root modal={modal} {...props} />
);
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
const DropdownMenuArrow = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Arrow>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Arrow>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Arrow
ref={ref}
className={cn(
"fill-white dark:fill-zinc-900 border-zinc-200 dark:border-zinc-800",
className,
)}
width={10}
height={5}
{...props}
/>
));
DropdownMenuArrow.displayName = DropdownMenuPrimitive.Arrow.displayName;
const DropdownMenuSearchInput = React.forwardRef<
HTMLInputElement,
React.InputHTMLAttributes<HTMLInputElement>
>(({ className, ...props }, ref) => (
<div className="px-2 py-1.5 border-b border-zinc-200 dark:border-zinc-800 mb-1 flex items-center gap-2">
<Icon
icon="hugeicons:search-01"
className="size-3.5 text-zinc-400 shrink-0"
/>
<input
ref={ref}
className={cn(
"w-full bg-transparent text-xs text-zinc-900 dark:text-zinc-100 placeholder:text-zinc-400 outline-none border-none",
className,
)}
{...props}
/>
</div>
));
DropdownMenuSearchInput.displayName = "DropdownMenuSearchInput";
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean;
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-pointer select-none items-center rounded-lg px-2.5 py-1.5 text-sm font-medium outline-none text-zinc-900 dark:text-zinc-100 focus:bg-zinc-100 dark:focus:bg-zinc-800 data-[state=open]:bg-zinc-100 dark:data-[state=open]:bg-zinc-800 transition-colors",
inset && "pl-8",
className,
)}
{...props}
>
{children}
<Icon
icon="hugeicons:arrow-right-01"
className="ml-auto size-4 text-zinc-400"
/>
</DropdownMenuPrimitive.SubTrigger>
));
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName;
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-xl border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-1.5 text-zinc-900 dark:text-zinc-100 shadow-xl data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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",
className,
)}
{...props}
/>
));
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName;
export interface DropdownMenuContentProps
extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {
showArrow?: boolean;
}
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
DropdownMenuContentProps
>(
(
{ className, sideOffset = 4, showArrow = false, children, ...props },
ref,
) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[9rem] overflow-hidden rounded-xl border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-1.5 text-zinc-900 dark:text-zinc-100 shadow-xl data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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",
className,
)}
{...props}
>
{showArrow && <DropdownMenuArrow />}
{children}
</DropdownMenuPrimitive.Content>
</DropdownMenuPrimitive.Portal>
),
);
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean;
color?: "default" | "danger";
}
>(({ className, inset, color = "default", ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-pointer select-none items-center rounded-lg px-2.5 py-1.5 text-sm font-medium outline-none transition-colors",
color === "danger"
? "text-rose-600 dark:text-rose-400 focus:bg-rose-50 dark:focus:bg-rose-950/40 focus:text-rose-600 dark:focus:text-rose-400"
: "text-zinc-900 dark:text-zinc-100 focus:bg-zinc-100 dark:focus:bg-zinc-800",
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className,
)}
{...props}
/>
));
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-pointer select-none items-center rounded-lg py-1.5 pl-8 pr-2.5 text-sm font-medium outline-none transition-colors text-zinc-900 dark:text-zinc-100 focus:bg-zinc-100 dark:focus:bg-zinc-800 data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
checked={checked}
{...props}
>
<span className="absolute left-2.5 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Icon
icon="hugeicons:tick-02"
className="size-4 text-sky-600 dark:text-sky-400"
/>
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
));
DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName;
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-pointer select-none items-center rounded-lg py-1.5 pl-8 pr-2.5 text-sm font-medium outline-none transition-colors text-zinc-900 dark:text-zinc-100 focus:bg-zinc-100 dark:focus:bg-zinc-800 data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
{...props}
>
<span className="absolute left-2.5 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Icon
icon="hugeicons:record"
className="size-2 fill-current text-sky-600 dark:text-sky-400"
/>
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
));
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn(
"px-2.5 py-1.5 text-xs font-semibold text-zinc-500 dark:text-zinc-400",
inset && "pl-8",
className,
)}
{...props}
/>
));
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator
ref={ref}
className={cn("-mx-1.5 my-1 h-px bg-zinc-200 dark:bg-zinc-800", className)}
{...props}
/>
));
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
const DropdownMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn(
"ml-auto text-xs tracking-widest text-zinc-400 dark:text-zinc-500 font-mono",
className,
)}
{...props}
/>
);
};
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
export {
DropdownMenu,
DropdownMenuArrow,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSearchInput,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
};
Default
A standard dropdown menu triggered by a button with menu labels, items, keyboard shortcuts, and dividers.
Checkboxes & Radio Item Groups
Supports toggleable checkbox items and single-selection radio groups inside the popover.
asChild: anyvariant: anybordered: anyw-56: anychecked: anyshowStatusBar: anyonCheckedChange: anysetShowStatusBar: any
Nested Submenus
Nest submenus recursively using DropdownMenuSub, DropdownMenuSubTrigger, and DropdownMenuSubContent.
asChild: anyvariant: anybordered: anyw-56: anyw-48: any
Search Filter Input & Custom Arrow Pointer
Pass showArrow on DropdownMenuContent to render a pointing arrow stem, and use DropdownMenuSearchInput to filter long list items dynamically.
showArrow: booleanDropdownMenuSearchInput
API Reference
Props — DropdownMenu
Sub-components for composing accessible Dropdown Menu overlays.
| Component | Description |
|---|---|
| DropdownMenuContent showArrow | Renders a directional pointing arrow stem pointing back to trigger element. |
| DropdownMenuSearchInput | Header input field for filtering items inside long dropdown menus. |
| DropdownMenuCheckboxItem | Menu item supporting checkable boolean state. |
| DropdownMenuRadioGroup / RadioItem | Group and item primitives for single choice radio selection. |
| DropdownMenuSub / SubTrigger / SubContent | Primitives for constructing recursive nested submenus. |