Dropdown
A dropdown menu component for displaying a list of actions or options. Supports keyboard navigation and outside click handling.
Installation
Terminal
TSXReactTailwind
npx @uiblox/cli add dropdownVisual Variations
Toggle between three different visual styles: default (shadow), minimal (flat), and bordered (thick border).
Basic menu items
Items with icons
Grouped sections
Basic Usage
Copy & paste ready
<Dropdown>
<DropdownTrigger className="inline-flex items-center justify-center px-4 py-2 text-sm font-medium border border-slate-300 dark:border-slate-700 rounded-md hover:bg-slate-100 dark:hover:bg-slate-800">
Open Menu
</DropdownTrigger>
<DropdownContent>
<DropdownItem>Profile</DropdownItem>
<DropdownItem>Settings</DropdownItem>
<DropdownSeparator />
<DropdownItem>Logout</DropdownItem>
</DropdownContent>
</Dropdown>With Labels and Groups
ReactTailwind CSSCopy & paste ready
<Dropdown>
<DropdownTrigger className="inline-flex items-center justify-center px-4 py-2 text-sm font-medium bg-purple-600 text-white rounded-md hover:bg-purple-700">
Account
</DropdownTrigger>
<DropdownContent>
<DropdownLabel>My Account</DropdownLabel>
<DropdownItem>Profile</DropdownItem>
<DropdownItem>Billing</DropdownItem>
<DropdownSeparator />
<DropdownLabel>Team</DropdownLabel>
<DropdownItem>Invite Members</DropdownItem>
<DropdownItem>Team Settings</DropdownItem>
</DropdownContent>
</Dropdown>Alignment
ReactTailwind CSSCopy & paste ready
<Dropdown>
<DropdownTrigger className="inline-flex items-center justify-center px-4 py-2 text-sm font-medium border border-slate-300 dark:border-slate-700 rounded-md hover:bg-slate-100 dark:hover:bg-slate-800">
Right Aligned
</DropdownTrigger>
<DropdownContent align="end">
<DropdownItem>Option 1</DropdownItem>
<DropdownItem>Option 2</DropdownItem>
</DropdownContent>
</Dropdown>Props
Dropdown
| Prop | Type | Default | Description |
|---|---|---|---|
| children* | React.ReactNode | - | The dropdown trigger and content |
| className | string | - | Additional CSS classes |
DropdownContent
| Prop | Type | Default | Description |
|---|---|---|---|
| align | "start" | "center" | "end" | "start" | Alignment of the dropdown content |
Source Code
Copy this code into src/components/ui/dropdown.tsx:
dropdown.tsx
TSXReactTailwind
"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
interface DropdownContextValue {
open: boolean;
setOpen: (open: boolean) => void;
}
const DropdownContext = React.createContext<DropdownContextValue | undefined>(undefined);
function useDropdownContext() {
const context = React.useContext(DropdownContext);
if (!context) {
throw new Error("Dropdown components must be used within a Dropdown provider");
}
return context;
}
const Dropdown = ({ children, className }: { children: React.ReactNode; className?: string }) => {
const [open, setOpen] = React.useState(false);
const dropdownRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
return (
<DropdownContext.Provider value={{ open, setOpen }}>
<div ref={dropdownRef} className={cn("relative inline-block", className)}>
{children}
</div>
</DropdownContext.Provider>
);
};
const DropdownTrigger = React.forwardRef<HTMLButtonElement, React.ButtonHTMLAttributes<HTMLButtonElement>>(
({ className, children, ...props }, ref) => {
const { open, setOpen } = useDropdownContext();
return (
<button ref={ref} onClick={() => setOpen(!open)} aria-expanded={open} className={className} {...props}>
{children}
</button>
);
}
);
const DropdownContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement> & { align?: "start" | "center" | "end" }>(
({ className, align = "start", ...props }, ref) => {
const { open } = useDropdownContext();
if (!open) return null;
return (
<div
ref={ref}
className={cn(
"absolute z-50 mt-2 min-w-[8rem] rounded-md border bg-white dark:bg-gray-900 p-1 shadow-md",
align === "start" && "left-0",
align === "end" && "right-0",
className
)}
{...props}
/>
);
}
);
const DropdownItem = React.forwardRef<HTMLButtonElement, React.ButtonHTMLAttributes<HTMLButtonElement>>(
({ className, ...props }, ref) => {
const { setOpen } = useDropdownContext();
return (
<button
ref={ref}
className={cn(
"flex w-full items-center rounded-sm px-2 py-1.5 text-sm hover:bg-gray-100 dark:hover:bg-gray-800",
className
)}
onClick={(e) => { props.onClick?.(e); setOpen(false); }}
{...props}
/>
);
}
);
export { Dropdown, DropdownTrigger, DropdownContent, DropdownItem };