@octavian-tocan/react-dropdown
v1.1.0
Published
A flexible, composable dropdown (select) component system for React with TypeScript support
Maintainers
Readme
@octavian-tocan/react-dropdown
A 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).
Features
- 🎯 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
Installation
npm install @octavian-tocan/react-dropdown
# or
pnpm add @octavian-tocan/react-dropdown
# or
yarn add @octavian-tocan/react-dropdownPeer Dependencies
This package requires:
react>= 18react-dom>= 18motion>= 12 (Motion for React - the latest version of Framer Motion)
Quick Start
Pre-made Searchable Dropdown
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 (
<Dropdown.Root
items={languages}
selectedItem={selectedLanguage}
onSelect={setSelectedLanguage}
getItemKey={(lang) => lang.code}
getItemDisplay={(lang) => lang.name}
>
<Dropdown.Trigger displayValue={selectedLanguage?.name || ''} />
<Dropdown.Searchable searchPlaceholder="Search languages..." />
</Dropdown.Root>
);
}Simple Dropdown (No Search)
<Dropdown.Root items={priorities} {...config}>
<Dropdown.Trigger displayValue={priority?.label || ''} />
<Dropdown.Simple />
</Dropdown.Root>Action Menu
import Dropdown from '@octavian-tocan/react-dropdown';
import { MoreHorizontal } from 'lucide-react';
function MenuExample() {
const menuItems = [
{ id: '1', label: 'Edit', icon: <Edit />, onClick: handleEdit },
{ id: '2', label: 'Delete', icon: <Trash />, onClick: handleDelete, showSeparator: true },
];
return (
<Dropdown.Menu
items={menuItems}
trigger={<MoreHorizontal />}
onSelect={(item) => item.onClick()}
getItemKey={(item) => item.id}
getItemDisplay={(item) => item.label}
getItemIcon={(item) => item.icon}
getItemSeparator={(item) => item.showSeparator ?? false}
/>
);
}API Reference
Compound Components
The package exports a compound component Dropdown with the following sub-components:
Dropdown.Root- Provider component that manages all stateDropdown.Trigger- Button that opens/closes the dropdownDropdown.Content- Container for custom compositionsDropdown.Search- Search input componentDropdown.List- Scrollable list of optionsDropdown.Simple- Pre-made dropdown with list onlyDropdown.Searchable- Pre-made dropdown with search + listDropdown.Menu- Action menu variant
Hooks
useDropdownContext<T>()- Access dropdown context (throws if used outside Root)useKeyboardNavigation<T>(items, getItemKey, onSelect, closeDropdown)- Keyboard navigation helpersuseClickOutside(ref, closeDropdown, isOpen)- Click outside detection
Types
All TypeScript types are exported. Key types include:
DropdownRootProps<T>DropdownTriggerPropsDropdownListProps<T>DropdownMenuProps<T>DropdownContextValue<T>DropdownSectionMetaDropdownPlacement
Advanced Usage
Custom Composition
Build your own dropdown layout:
<Dropdown.Content>
<CustomHeader />
<Dropdown.Search placeholder="Filter..." />
<Dropdown.List />
<CustomFooter />
</Dropdown.Content>Sections and Grouping
Group items into sections with headers:
<Dropdown.Root
items={items}
getItemSection={(item) => ({
key: item.category,
label: item.category,
icon: '📁',
})}
// ...
>Icons and Descriptions
Add icons and descriptions to items:
<Dropdown.Root
items={items}
getItemIcon={(item) => <Icon name={item.icon} />}
getItemDescription={(item) => item.description}
// ...
>Portal Rendering
Render dropdown in a portal to avoid overflow clipping:
<Dropdown.Root
items={items}
usePortal={true}
triggerRef={triggerRef}
// ...
>Placement Control
Control dropdown placement (top or bottom):
<Dropdown.Root
items={items}
dropdownPlacement="top" // or "bottom" (default)
// ...
>Hiding Search for Small Lists
Hide search input for small lists:
<Dropdown.Searchable hideSearchThreshold={4} />When items.length <= hideSearchThreshold, search is hidden.
Examples
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
Development
# Install dependencies
pnpm install
# Build
pnpm build
# Type check
pnpm typecheck
# Run tests
pnpm test
# Watch mode
pnpm devLicense
MIT
