Docs
Dropdown Menu
Dropdown Menu
Displays a menu to the user — such as a set of actions or functions — triggered by a button.
Loading...
Installation
Install the following dependencies:
pnpm add @radix-ui/react-dropdown-menu
Copy and paste the following code into your project.
"use client";
import * as React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import type { VariantProps } from "cva";
import { cva } from "cva";
import { Check, ChevronRight, Circle } from "lucide-react";
import { autoRef, cn } from "@/lib/utils";
/* -------------------------------------------------------------------------- */
/* Variants */
/* -------------------------------------------------------------------------- */
export const dropdownMenuVariants = {
content: cva({
base: cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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",
),
}),
item: cva({
base: cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors",
"focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
),
variants: {
inset: {
true: "pl-8",
false: "",
},
},
defaultVariants: {
inset: false,
},
}),
checkboxItem: cva({
base: cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors",
"focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
),
}),
radioItem: cva({
base: cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors",
"focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
),
}),
label: cva({
base: "px-2 py-1.5 text-sm font-semibold",
variants: {
inset: {
true: "pl-8",
false: "",
},
},
defaultVariants: {
inset: false,
},
}),
separator: cva({
base: "-mx-1 my-1 h-px bg-muted",
}),
shortcut: cva({
base: "ml-auto text-xs tracking-widest opacity-60",
}),
subTrigger: cva({
base: "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
variants: {
inset: {
true: "pl-8",
false: "",
},
},
defaultVariants: {
inset: false,
},
}),
subContent: cva({
base: cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg",
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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",
),
}),
};
/* -------------------------------------------------------------------------- */
/* Components */
/* -------------------------------------------------------------------------- */
export const DropdownMenu = DropdownMenuPrimitive.Root;
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
export const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
export const DropdownMenuSub = DropdownMenuPrimitive.Sub;
export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
/* ------------------------------- SubTrigger ------------------------------- */
export type DropdownMenuSubTriggerProps = React.ComponentPropsWithRef<
typeof DropdownMenuPrimitive.SubTrigger
> &
VariantProps<typeof dropdownMenuVariants.subTrigger>;
export const DropdownMenuSubTrigger = autoRef(
({ className, inset, children, ...props }: DropdownMenuSubTriggerProps) => {
return (
<DropdownMenuPrimitive.SubTrigger
className={cn(dropdownMenuVariants.subTrigger({ inset }), className)}
{...props}
>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
);
},
);
/* ------------------------------- SubContent ------------------------------- */
export type DropdownMenuSubContentProps = React.ComponentPropsWithRef<
typeof DropdownMenuPrimitive.SubContent
>;
export const DropdownMenuSubContent = autoRef(
({ className, ...props }: DropdownMenuSubContentProps) => {
return (
<DropdownMenuPrimitive.SubContent
className={cn(dropdownMenuVariants.subContent(), className)}
{...props}
/>
);
},
);
/* --------------------------------- Content -------------------------------- */
export type DropdownMenuContentProps = React.ComponentPropsWithRef<
typeof DropdownMenuPrimitive.Content
>;
export const DropdownMenuContent = autoRef(
({ className, sideOffset = 4, ...props }: DropdownMenuContentProps) => {
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
sideOffset={sideOffset}
className={cn(dropdownMenuVariants.content(), className)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
);
},
);
/* ---------------------------------- Item ---------------------------------- */
export type DropdownMenuItemProps = React.ComponentPropsWithRef<
typeof DropdownMenuPrimitive.Item
> &
VariantProps<typeof dropdownMenuVariants.item>;
export const DropdownMenuItem = autoRef(
({ className, inset, ...props }: DropdownMenuItemProps) => {
return (
<DropdownMenuPrimitive.Item
className={cn(dropdownMenuVariants.item({ inset }), className)}
{...props}
/>
);
},
);
/* ------------------------------ Checkbox Item ----------------------------- */
export type DropdownMenuCheckboxItemProps = React.ComponentPropsWithRef<
typeof DropdownMenuPrimitive.CheckboxItem
>;
export const DropdownMenuCheckboxItem = autoRef(
({
className,
children,
checked,
...props
}: DropdownMenuCheckboxItemProps) => {
return (
<DropdownMenuPrimitive.CheckboxItem
className={cn(dropdownMenuVariants.checkboxItem(), className)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
);
},
);
/* ------------------------------- Radio Item ------------------------------- */
export type DropdownMenuRadioItem = React.ComponentPropsWithRef<
typeof DropdownMenuPrimitive.RadioItem
>;
export const DropdownMenuRadioItem = autoRef(
({ className, children, ...props }: DropdownMenuRadioItem) => {
return (
<DropdownMenuPrimitive.RadioItem
className={cn(dropdownMenuVariants.radioItem(), className)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
);
},
);
/* --------------------------------- Label ---------------------------------- */
export type DropdownMenuLabelProps = React.ComponentPropsWithRef<
typeof DropdownMenuPrimitive.Label
> &
VariantProps<typeof dropdownMenuVariants.label>;
export const DropdownMenuLabel = autoRef(
({ className, inset, ...props }: DropdownMenuLabelProps) => {
return (
<DropdownMenuPrimitive.Label
className={cn(dropdownMenuVariants.label({ inset }), className)}
{...props}
/>
);
},
);
/* -------------------------------- Separator ------------------------------- */
export type DropdownMenuSeparatorProps = React.ComponentPropsWithRef<
typeof DropdownMenuPrimitive.Separator
>;
export const DropdownMenuSeparator = autoRef(
({ className, ...props }: DropdownMenuSeparatorProps) => {
return (
<DropdownMenuPrimitive.Separator
className={cn(dropdownMenuVariants.separator(), className)}
{...props}
/>
);
},
);
/* -------------------------------- Shortcut -------------------------------- */
export type DropdownMenuShortcutProps = React.ComponentPropsWithRef<"span">;
export const DropdownMenuShortcut = ({
className,
...props
}: DropdownMenuShortcutProps) => {
return (
<span
className={cn(dropdownMenuVariants.shortcut(), className)}
{...props}
/>
);
};
Update the import paths to match your project setup.
Examples
Dialog from Dropdown Menu
Loading...