A powerful, customizable React select/combobox component built with FluentUI, supporting single and multiple selection, grouping, filtering, and keyboard navigation.
npm install react-selectifyA powerful, customizable React select/combobox component built with FluentUI, supporting single and multiple selection, grouping, filtering, and keyboard navigation.
- ✅ Single and multiple selection modes
- ✅ Option grouping support
- ✅ Real-time filtering/search
- ✅ Keyboard navigation (Arrow keys, Enter, Space, Escape)
- ✅ Dynamic dropdown positioning (auto top/bottom)
- ✅ Customizable styling
- ✅ TypeScript support
- ✅ Accessible (ARIA labels)
- ✅ Performance optimized with React.memo
``bash`
npm install react-selectify
`tsx
import React, { useState } from 'react';
import { ReactSelectify, Option } from 'react-selectify';
function App() {
const [selectedKeys, setSelectedKeys] = useState
const options: Option[] = [
{ key: 'option1', text: 'Option 1' },
{ key: 'option2', text: 'Option 2' },
{ key: 'option3', text: 'Option 3' },
];
return (
placeholder="Select an option..."
selectedKeys={selectedKeys}
onChange={(event, option) => {
if (option) {
setSelectedKeys([option.key]);
}
}}
/>
);
}
`
`tsx
import React, { useState } from 'react';
import { ReactSelectify, Option } from 'react-selectify';
function App() {
const [selectedKeys, setSelectedKeys] = useState
const options: Option[] = [
{ key: 'apple', text: 'Apple' },
{ key: 'banana', text: 'Banana' },
{ key: 'orange', text: 'Orange' },
];
return (
multiple
placeholder="Select multiple options..."
selectedKeys={selectedKeys}
onChange={(event, option) => {
if (option) {
const newKeys = selectedKeys.includes(option.key)
? selectedKeys.filter(k => k !== option.key)
: [...selectedKeys, option.key];
setSelectedKeys(newKeys);
}
}}
/>
);
}
`
`tsx
import React, { useState } from 'react';
import { ReactSelectify, OptionGroup } from 'react-selectify';
function App() {
const [selectedKeys, setSelectedKeys] = useState
const groups: OptionGroup[] = [
{
label: 'Fruits',
options: [
{ key: 'apple', text: 'Apple' },
{ key: 'banana', text: 'Banana' },
],
},
{
label: 'Vegetables',
options: [
{ key: 'carrot', text: 'Carrot' },
{ key: 'broccoli', text: 'Broccoli' },
],
},
];
return (
multiple
placeholder="Select items..."
selectedKeys={selectedKeys}
onChange={(event, option) => {
if (option) {
const newKeys = selectedKeys.includes(option.key)
? selectedKeys.filter(k => k !== option.key)
: [...selectedKeys, option.key];
setSelectedKeys(newKeys);
}
}}
/>
);
}
`
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| options | Option[] | [] | List of options when not using groups |groups
| | OptionGroup[] | [] | List of option groups for grouping options |selectedKeys
| | string[] | undefined | List of selected option keys (controlled) |onChange
| | (event?, option?) => void | - | Callback triggered when selection changes |disabled
| | boolean | false | Whether the component is disabled |placeholder
| | string | - | Placeholder text displayed in the input |positionOffset
| | 'top' \| 'bottom' | 'bottom' | Position of the dropdown (auto-calculated if not enough space) |styles
| | { [key: string]: React.CSSProperties } | {} | Inline styles for customizing appearance |showTooltip
| | boolean | false | Whether to show full option text as tooltip on hover |className
| | string | '' | Additional CSS class for the root element |multiple
| | boolean | false | Whether multiple selections are allowed |renderOption
| | (props: any) => React.ReactNode | - | Custom render function for each option |
`typescript`
interface Option {
key: string; // Unique identifier
text: string; // Display text
disabled?: boolean; // Whether the option is disabled
data?: any; // Additional data
selected?: boolean; // Whether the option is selected (internal use)
}
`typescript`
interface OptionGroup {
label: string; // Group label
options: Option[]; // Options in this group
}
`tsx`
styles={{
root: { width: '300px' },
input: { fontSize: '16px' },
callOut: { maxWidth: '400px' },
groupLabel: { color: '#0078d4' },
}}
/>
`tsx
renderOption={({ option, selected, highlighted }) => (
padding: '8px',
backgroundColor: highlighted ? '#e1f5ff' : 'transparent',
fontWeight: selected ? 'bold' : 'normal'
}}>
{option.text}
$3
You can use the component without providing
selectedKeys prop. The component will manage its own internal state:`tsx
options={options}
multiple
onChange={(event, option) => {
console.log('Selected:', option);
}}
/>
`Keyboard Navigation
- Arrow Up/Down: Navigate through options
- Enter: Select highlighted option
- Space: Select highlighted option (or type to search)
- Escape: Close dropdown
- Type to filter: Start typing to filter options
Styling
The component uses CSS classes that you can override:
-
.hh-Multi-Select - Root container
- .hh-Input-Wrapper - Input wrapper
- .hh-Input-Display - Input field
- .hh-Dropdown - Dropdown container
- .hh-Options-List - Options list container
- .hh-Option-Item - Individual option item
- .hh-Option-Item.highlighted - Highlighted option
- .hh-Option-Item.disabled - Disabled option
- .hh-Option-Group - Option group container
- .hh-Option-GroupLabel - Group label
- .hh-No-Result` - No results message- React 16.8+ (with hooks support)
- @fluentui/react ^8.0.0
- @fluentui/font-icons-mdl2 ^8.0.0
MIT
Contributions are welcome! Please feel free to submit a Pull Request.
https://github.com/hienpham123/react-selectify