A flexible, composable dropdown (select) component system for React with TypeScript support
npm install @octavian-tocan/react-dropdownA flexible, composable dropdown (select) component system for React with TypeScript support. Built with accessibility in mind and featuring smooth animations powered by Motion (the latest version of Framer Motion).
- 🎯 Composable API - Mix and match components or use pre-made convenience components
- 🔍 Searchable - Built-in search/filter functionality
- ♿ Accessible - Proper ARIA attributes and keyboard navigation
- 🎨 Customizable - Support for icons, descriptions, sections, and custom styling
- 📱 Portal Support - Render dropdowns in portals to avoid overflow clipping
- 🎭 Type-Safe - Full TypeScript support with generics
- ⚡ Performant - Optimized with React hooks and memoization
``bash`
npm install @octavian-tocan/react-dropdownor
pnpm add @octavian-tocan/react-dropdownor
yarn add @octavian-tocan/react-dropdown
This package requires:
- react >= 18react-dom
- >= 18motion
- >= 12 (Motion for React - the latest version of Framer Motion)
`tsx
import Dropdown from '@octavian-tocan/react-dropdown';
function MyComponent() {
const [selectedLanguage, setSelectedLanguage] = useState(null);
const languages = [
{ code: 'en', name: 'English' },
{ code: 'es', name: 'Spanish' },
{ code: 'fr', name: 'French' },
];
return (
selectedItem={selectedLanguage}
onSelect={setSelectedLanguage}
getItemKey={(lang) => lang.code}
getItemDisplay={(lang) => lang.name}
>
);
}
`
`tsx`
`tsx
import Dropdown from '@octavian-tocan/react-dropdown';
import { MoreHorizontal } from 'lucide-react';
function MenuExample() {
const menuItems = [
{ id: '1', label: 'Edit', icon:
{ id: '2', label: 'Delete', icon:
];
return (
trigger={
onSelect={(item) => item.onClick()}
getItemKey={(item) => item.id}
getItemDisplay={(item) => item.label}
getItemIcon={(item) => item.icon}
getItemSeparator={(item) => item.showSeparator ?? false}
/>
);
}
`
The package exports a compound component Dropdown with the following sub-components:
- Dropdown.Root - Provider component that manages all state
- Dropdown.Trigger - Button that opens/closes the dropdown
- Dropdown.Content - Container for custom compositions
- Dropdown.Search - Search input component
- Dropdown.List - Scrollable list of options
- Dropdown.Simple - Pre-made dropdown with list only
- Dropdown.Searchable - Pre-made dropdown with search + list
- Dropdown.Menu - Action menu variant
- useDropdownContext
- useKeyboardNavigation
- useClickOutside(ref, closeDropdown, isOpen) - Click outside detection
All TypeScript types are exported. Key types include:
- DropdownRootPropsDropdownTriggerProps
- DropdownListProps
- DropdownMenuProps
- DropdownContextValue
- DropdownSectionMeta
- DropdownPlacement
-
Build your own dropdown layout:
`tsx`
Group items into sections with headers:
`tsx`
getItemSection={(item) => ({
key: item.category,
label: item.category,
icon: '📁',
})}
// ...
>
Add icons and descriptions to items:
`tsx`
getItemIcon={(item) =>
getItemDescription={(item) => item.description}
// ...
>
Render dropdown in a portal to avoid overflow clipping:
`tsx`
usePortal={true}
triggerRef={triggerRef}
// ...
>
Control dropdown placement (top or bottom):
`tsx`
dropdownPlacement="top" // or "bottom" (default)
// ...
>
Hide search input for small lists:
`tsx`
When items.length <= hideSearchThreshold, search is hidden.
See the Storybook stories for comprehensive examples covering:
- Simple dropdowns
- Searchable dropdowns
- Action menus
- Custom compositions
- Sections and grouping
- Icons and descriptions
- Disabled items
- Portal rendering
- Placement options
`bashInstall dependencies
pnpm install
MIT