A customizable and feature-rich selector component for React Native with Expo support.
| V2.0 Docs | Old V1 Docs |
- π¨ Version 2.0 - Breaking Changes
- π¦ Installation
- β‘ Quick Start
- π² Examples
- β¨ Features
- π§© API Reference
- π¦ Usage Examples
- π Migration from v1.x to v2.0
- π€ Contributing
- π License
If you're upgrading from v1.x, please read the Migration Guide below.
Version 2.0 introduces a cleaner, more organized API with grouped configuration objects. See CHANGELOG.md for complete details.
π Looking for v1 docs? If you're still using v1.x and need the old documentation, check out README_v1.md
β οΈ Version 2.0 is a breaking release.
This README documents v2.x. If you're still using v1.x, see the legacy docs:
- v2.x (current) β You are here
- v1.x Documentation β For users still on version 1.x
npm install rn-selector@2.0.0
# or
yarn add rn-selector@2.0.0import React, { useState } from 'react';
import { Selector } from 'rn-selector';
const options = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange', disabled: true },
];
export default function App() {
const [selectedValue, setSelectedValue] = useState('');
return (
<Selector
options={options}
selectedValue={selectedValue}
onValueChange={(value) => setSelectedValue(value)}
placeholder="Select a fruit"
searchConfig={{ searchable: true }}
theme={{ primaryColor: '#1976d2' }}
/>
);
}- π Searchable: Optional search functionality to filter options
- β Create Elements: (v2.0) - Create new options when search returns no results
- π¨ Highly Customizable: Grouped configuration objects for cleaner code
- π± Cross-platform: Works on iOS, Android, and Web
- βΏ Accessible: Built with accessibility in mind
- π TypeScript Support: Full TypeScript support with enhanced IntelliSense
- π― Expo Compatible: Works seamlessly with Expo SDK 54+
- π§ Flexible: Custom render functions for options and selected values
- π― Multiple Selection: Support for single and multiple selection modes
- π Modal Positioning: Choose between center or bottom modal positioning
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
options |
SelectorOption[] |
β | - | Array of options. Each: {label: string, value: any, disabled?: boolean} |
selectedValue |
any |
β | - | Selected value(s). For multiple selection, pass an array |
onValueChange |
(value: any, option: SelectorOption | SelectorOption[]) => void |
β | - | Callback when selection changes |
placeholder |
string |
β | "Select an option" |
Placeholder text when nothing is selected |
disabled |
boolean |
β | false |
Disable the entire selector |
multiple |
boolean |
β | false |
Enable multiple selection mode |
Group all styling customizations in one object.
| Property | Type | Description |
|---|---|---|
container |
ViewStyle |
Main wrapper container style |
button |
ViewStyle |
Selector button style (replaces v1 style) |
dropdown |
ViewStyle |
Dropdown modal container style |
optionItem |
ViewStyle |
Individual option item style |
selectedOptionItem |
ViewStyle |
Selected option item style |
text |
TextStyle |
Option and selected text style |
placeholderText |
TextStyle |
Placeholder text style |
searchInput |
TextStyle |
Search input field style |
Example:
<Selector
styles={{
container: { marginVertical: 10 },
button: { borderRadius: 12, borderColor: '#1976d2' },
dropdown: { maxHeight: 400 },
text: { fontSize: 16, fontWeight: '500' }
}}
/>Configure search functionality.
| Property | Type | Default | Description |
|---|---|---|---|
searchable |
boolean |
false |
Enable search functionality |
placeholder |
string |
"Search..." |
Search input placeholder |
placeholderTextColor |
string |
"#a2a2a2" |
Search placeholder color |
noResultsText |
string |
"No matches found" |
Text when no results |
Example:
<Selector
searchConfig={{
searchable: true,
placeholder: "Type to filter...",
noResultsText: "No items found"
}}
/>Configure modal behavior and appearance.
| Property | Type | Default | Description |
|---|---|---|---|
position |
'center' | 'bottom' |
'center' |
Modal position |
overlayColor |
string |
'rgba(0,0,0,0.5)' |
Overlay background |
maxDropdownHeight |
number |
screenHeight * 0.5 |
Max dropdown height |
confirmText |
string |
'Done' |
Done button text (multiple mode) |
Example:
<Selector
modalConfig={{
position: "bottom",
overlayColor: "rgba(0, 0, 0, 0.7)",
maxDropdownHeight: 500,
confirmText: "Apply Selection"
}}
/>Visual theme customization.
| Property | Type | Default | Description |
|---|---|---|---|
primaryColor |
string |
'#1976d2' |
Primary color for selections |
checkIcon |
ReactNode |
β |
Custom checkmark icon |
arrowIcon |
ReactNode |
βΌ |
Custom dropdown arrow |
Example:
<Selector
theme={{
primaryColor: "#FF5722",
checkIcon: <Icon name="check" size={16} />,
arrowIcon: <Icon name="chevron-down" size={12} />
}}
/>Enable creating new options when search returns no results.
| Property | Type | Required | Description |
|---|---|---|---|
enabled |
boolean |
β | Enable the feature |
text |
string |
β | Button text |
onPress |
(searchTerm: string) => void |
β | Callback with search term |
style |
ViewStyle |
β | Custom button style |
textStyle |
TextStyle |
β | Custom button text style |
Example:
const [fruits, setFruits] = useState([
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' }
]);
<Selector
options={fruits}
selectedValue={selected}
onValueChange={setSelected}
searchConfig={{ searchable: true }}
createConfig={{
enabled: true,
text: "β Add new fruit",
onPress: (searchTerm) => {
const newFruit = {
label: searchTerm,
value: searchTerm.toLowerCase().replace(/\s+/g, '-')
};
setFruits([...fruits, newFruit]);
setSelected(newFruit.value);
}
}}
/>Behavior:
- Only appears when
searchConfig.searchableistrue - Only shows when there's a search term and no matching results
- Modal closes automatically after
onPressis called
| Prop | Type | Description |
|---|---|---|
renderOption |
(option, isSelected, onClose?) => ReactNode |
Custom option renderer |
renderSelectedOption |
(option, selectedOptions?) => ReactNode |
Custom selected value display |
<Selector
options={[
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
]}
selectedValue={selectedValue}
onValueChange={setSelectedValue}
placeholder="Choose an option"
/><Selector
options={countries}
selectedValue={selectedCountry}
onValueChange={setSelectedCountry}
searchConfig={{
searchable: true,
placeholder: "Search countries...",
noResultsText: "No countries found"
}}
/><Selector
options={options}
selectedValue={selectedValues} // Array
onValueChange={setSelectedValues}
multiple={true}
modalConfig={{
position: "bottom",
confirmText: "Apply"
}}
theme={{
primaryColor: "#FF5722"
}}
/><Selector
options={options}
selectedValue={selectedValue}
onValueChange={setSelectedValue}
styles={{
container: { marginBottom: 20 },
button: {
borderRadius: 12,
borderColor: '#1976d2',
borderWidth: 2,
paddingVertical: 16
},
dropdown: {
borderRadius: 12,
maxHeight: 400
},
text: {
fontSize: 16,
fontWeight: '500'
},
placeholderText: {
color: '#aaa',
fontStyle: 'italic'
}
}}
theme={{
primaryColor: '#1976d2'
}}
/>const [fruits, setFruits] = useState([
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' }
]);
const [selected, setSelected] = useState('');
<Selector
options={fruits}
selectedValue={selected}
onValueChange={setSelected}
placeholder="Select or create a fruit"
searchConfig={{
searchable: true,
placeholder: "Search or type new fruit..."
}}
createConfig={{
enabled: true,
text: "β Add new fruit",
onPress: (searchTerm) => {
const newFruit = {
label: searchTerm,
value: searchTerm.toLowerCase().replace(/\s+/g, '-')
};
setFruits([...fruits, newFruit]);
setSelected(newFruit.value);
console.log('Created:', newFruit);
},
style: { backgroundColor: '#4CAF50' }
}}
/><Selector
options={users}
selectedValue={selectedUser}
onValueChange={setSelectedUser}
renderOption={(option, isSelected, onClose) => (
<TouchableOpacity
style={{
padding: 15,
backgroundColor: isSelected ? '#e3f2fd' : 'white',
flexDirection: 'row',
alignItems: 'center',
}}
onPress={() => {
setSelectedUser(option.value);
onClose?.();
}}
>
<Image
source={{ uri: option.avatar }}
style={{ width: 32, height: 32, borderRadius: 16, marginRight: 12 }}
/>
<View>
<Text style={{ fontWeight: '600' }}>{option.label}</Text>
<Text style={{ fontSize: 12, color: '#666' }}>{option.email}</Text>
</View>
{isSelected && <Icon name="check" size={20} color="#1976d2" />}
</TouchableOpacity>
)}
/><Selector
options={options}
selectedValue={selectedValue}
onValueChange={setSelectedValue}
multiple={true}
renderSelectedOption={(option, selectedOptions) => (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
{selectedOptions?.length > 0 ? (
<>
<Text style={{ flex: 1 }}>
{selectedOptions.map(opt => opt.label).join(', ')}
</Text>
<Badge count={selectedOptions.length} />
</>
) : (
<Text style={{ color: '#999' }}>Select items</Text>
)}
</View>
)}
/>1. Update the package:
npm install rn-selector@2.0.0
# or
yarn add rn-selector@2.0.02. Refactor style props into styles object:
// β Before (v1.x)
<Selector
style={{ borderRadius: 12 }}
containerStyle={{ margin: 10 }}
dropdownStyle={{ maxHeight: 400 }}
optionStyle={{ padding: 20 }}
selectedOptionStyle={{ backgroundColor: '#f0f0f0' }}
textStyle={{ fontSize: 16 }}
placeholderTextStyle={{ color: '#aaa' }}
searchInputStyle={{ borderRadius: 8 }}
/>
// β
After (v2.0)
<Selector
styles={{
button: { borderRadius: 12 },
container: { margin: 10 },
dropdown: { maxHeight: 400 },
optionItem: { padding: 20 },
selectedOptionItem: { backgroundColor: '#f0f0f0' },
text: { fontSize: 16 },
placeholderText: { color: '#aaa' },
searchInput: { borderRadius: 8 }
}}
/>3. Group search props into searchConfig:
// β Before (v1.x)
<Selector
searchable={true}
searchPlaceholder="Search..."
placeholderSearchTextColor="#a2a2a2"
noResultsText="Nothing found"
/>
// β
After (v2.0)
<Selector
searchConfig={{
searchable: true,
placeholder: "Search...",
placeholderTextColor: "#a2a2a2",
noResultsText: "Nothing found"
}}
/>4. Group modal props into modalConfig:
// β Before (v1.x)
<Selector
modalPosition="bottom"
modalBackgroundColor="rgba(0, 0, 0, 0.7)"
maxHeight={500}
doneButtonText="Apply"
/>
// β
After (v2.0)
<Selector
modalConfig={{
position: "bottom",
overlayColor: "rgba(0, 0, 0, 0.7)",
maxDropdownHeight: 500,
confirmText: "Apply"
}}
/>5. Group theme props into theme:
// β Before (v1.x)
<Selector
primaryColor="#FF5722"
iconCheck={<CheckIcon />}
customArrow={<ArrowIcon />}
/>
// β
After (v2.0)
<Selector
theme={{
primaryColor: "#FF5722",
checkIcon: <CheckIcon />,
arrowIcon: <ArrowIcon />
}}
/>6. Complete migration example:
// β Before (v1.x)
<Selector
options={options}
selectedValue={selected}
onValueChange={setSelected}
placeholder="Choose"
searchable={true}
searchPlaceholder="Filter..."
modalPosition="bottom"
primaryColor="#1976d2"
style={{ borderRadius: 8 }}
containerStyle={{ marginBottom: 20 }}
textStyle={{ fontSize: 16 }}
doneButtonText="Done"
/>
// β
After (v2.0)
<Selector
options={options}
selectedValue={selected}
onValueChange={setSelected}
placeholder="Choose"
searchConfig={{
searchable: true,
placeholder: "Filter..."
}}
modalConfig={{
position: "bottom",
confirmText: "Done"
}}
theme={{
primaryColor: "#1976d2"
}}
styles={{
button: { borderRadius: 8 },
container: { marginBottom: 20 },
text: { fontSize: 16 }
}}
/>| v1 Prop | v2 Equivalent |
|---|---|
searchable |
searchConfig.searchable |
searchPlaceholder |
searchConfig.placeholder |
placeholderSearchTextColor |
searchConfig.placeholderTextColor |
noResultsText |
searchConfig.noResultsText |
modalPosition |
modalConfig.position |
modalBackgroundColor |
modalConfig.overlayColor |
maxHeight |
modalConfig.maxDropdownHeight |
doneButtonText |
modalConfig.confirmText |
primaryColor |
theme.primaryColor |
iconCheck |
theme.checkIcon |
customArrow |
theme.arrowIcon |
style |
styles.button |
containerStyle |
styles.container |
dropdownStyle |
styles.dropdown |
optionStyle |
styles.optionItem |
selectedOptionStyle |
styles.selectedOptionItem |
textStyle |
styles.text |
placeholderTextStyle |
styles.placeholderText |
searchInputStyle |
styles.searchInput |
See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
Made with create-react-native-library
- Built for real apps β Handles single and multiple selection, search, custom rendering and theming without getting in your way.
- Clean, grouped API β Configuration lives in
styles,theme,modalConfig,searchConfig, andcreateConfig, not in a long list of flat props. - Expo & RN friendly β Works out of the box with React Native CLI and Expo projects (including SDK 54+).
- TypeScript-first β Strong types and IntelliSense make it obvious what each prop does.
- Highly customizable β Override layout, colors, icons, and option rendering to match any design system.
- Create-as-you-type β Let users add new options when search returns no results, with full control over the creation logic.
- Small but powerful β Focused on one job (selection) and does it well, without pulling in heavy native dependencies.

