react-search-select-dropdown
v1.5.0
Published
A customizable React autocomplete/search select dropdown component with TypeScript support
Maintainers
Readme
React Search Select Dropdown
A highly customizable React autocomplete/search select dropdown component with TypeScript support and inline styles.
Features
- 🔍 Search functionality - Filter options as you type
- ⌨️ Keyboard navigation - Navigate with arrow keys, select with Enter
- 🎨 Fully customizable styling - Override default inline styles with
customStylesprop - 🎭 Custom input support - Use any UI library (Material-UI, Ant Design, etc.) via
renderInputprop - 🔤 TypeScript support - Full type definitions included
- 🎯 Generic types - Support for any value type (string, number, objects)
- 🌐 i18n ready - Customizable text for localization
- 📱 Responsive design - Works on all screen sizes
- ♿ Accessible - ARIA labels and keyboard support
- 💅 Inline styles - No CSS conflicts, minimal external CSS (only scrollbar)
Installation
npm install react-search-select-dropdownor
yarn add react-search-select-dropdownor
pnpm add react-search-select-dropdownQuick Start
import { Autocomplete } from "react-search-select-dropdown";
import type { AutocompleteOption } from "react-search-select-dropdown";
import "react-search-select-dropdown/styles.css";
const options: AutocompleteOption[] = [
{ value: "us", label: "United States" },
{ value: "ca", label: "Canada" },
{ value: "mx", label: "Mexico" },
];
function App() {
return (
<Autocomplete
options={options}
placeholder="Search country..."
onSelect={(option) => console.log("Selected:", option)}
/>
);
}Usage Examples
Example 1: String Values (Default)
import { useState } from "react";
import { Autocomplete } from "react-search-select-dropdown";
import type { AutocompleteOption } from "react-search-select-dropdown";
import "react-search-select-dropdown/styles.css";
const countryOptions: AutocompleteOption[] = [
{ value: "us", label: "United States" },
{ value: "ca", label: "Canada" },
{ value: "mx", label: "Mexico" },
{ value: "gb", label: "United Kingdom" },
{ value: "fr", label: "France" },
{ value: "de", label: "Germany" },
{ value: "it", label: "Italy" },
{ value: "es", label: "Spain" },
];
function App() {
const [selectedCountry, setSelectedCountry] =
useState<AutocompleteOption | null>(null);
return (
<div>
<Autocomplete
options={countryOptions}
placeholder="Search country..."
onSelect={(option) => {
setSelectedCountry(option);
console.log("Selected country:", option);
}}
customStyles={{
input: {
borderColor: "gray",
},
dropdown: {
maxHeight: "300px",
},
option: {
padding: "8px 16px",
fontSize: "16px",
},
}}
/>
{selectedCountry && (
<div
style={{
marginTop: "12px",
padding: "12px",
backgroundColor: "#f0fdf4",
border: "1px solid #86efac",
borderRadius: "6px",
color: "#166534",
fontSize: "14px",
lineHeight: "1.6",
}}
>
<strong>Selected:</strong> {selectedCountry.label} (code: {selectedCountry.value})
</div>
)}
</div>
);
}Example 2: Number Values
import { useState } from "react";
import { Autocomplete } from "react-search-select-dropdown";
import type { AutocompleteOption } from "react-search-select-dropdown";
import "react-search-select-dropdown/styles.css";
const numberOptions: AutocompleteOption<number>[] = [
{ value: 1, label: "Option One" },
{ value: 2, label: "Option Two" },
{ value: 3, label: "Option Three" },
{ value: 4, label: "Option Four" },
{ value: 5, label: "Option Five" },
];
function App() {
const [selectedNumber, setSelectedNumber] =
useState<AutocompleteOption<number> | null>(null);
return (
<div>
<Autocomplete
options={numberOptions}
placeholder="Search option..."
onSelect={(option) => {
setSelectedNumber(option);
console.log("Selected number:", option);
}}
customStyles={{
input: {
borderColor: "gray",
},
dropdown: {
maxHeight: "300px",
},
option: {
padding: "8px 16px",
fontSize: "16px",
},
}}
/>
{selectedNumber && (
<div
style={{
marginTop: "12px",
padding: "12px",
backgroundColor: "#f0fdf4",
border: "1px solid #86efac",
borderRadius: "6px",
color: "#166534",
fontSize: "14px",
lineHeight: "1.6",
}}
>
<strong>Selected:</strong> {selectedNumber.label} (ID: {selectedNumber.value})
</div>
)}
</div>
);
}Example 3: Custom Object Values
import { useState } from "react";
import { Autocomplete } from "react-search-select-dropdown";
import type { AutocompleteOption } from "react-search-select-dropdown";
import "react-search-select-dropdown/styles.css";
interface User {
id: number;
email: string;
role: string;
}
const userOptions: AutocompleteOption<User>[] = [
{
value: { id: 1, email: "[email protected]", role: "admin" },
label: "John Doe (Admin)",
},
{
value: { id: 2, email: "[email protected]", role: "user" },
label: "Jane Smith (User)",
},
{
value: { id: 3, email: "[email protected]", role: "moderator" },
label: "Bob Johnson (Moderator)",
},
{
value: { id: 4, email: "[email protected]", role: "user" },
label: "Alice Williams (User)",
},
];
function App() {
const [selectedUser, setSelectedUser] =
useState<AutocompleteOption<User> | null>(null);
return (
<div>
<Autocomplete
options={userOptions}
placeholder="Search user..."
getOptionKey={(option) => option.value.id}
onSelect={(option) => {
setSelectedUser(option);
console.log("Selected user:", option);
}}
customStyles={{
input: {
borderColor: "gray",
},
dropdown: {
maxHeight: "300px",
},
option: {
padding: "8px 16px",
fontSize: "16px",
},
}}
/>
{selectedUser && (
<div
style={{
marginTop: "12px",
padding: "12px",
backgroundColor: "#f0fdf4",
border: "1px solid #86efac",
borderRadius: "6px",
color: "#166534",
fontSize: "14px",
lineHeight: "1.6",
}}
>
<strong>Selected User:</strong>
<br />
Name: {selectedUser.label}
<br />
ID: {selectedUser.value.id}
<br />
Email: {selectedUser.value.email}
<br />
Role: {selectedUser.value.role}
</div>
)}
</div>
);
}Customization
Custom Styles
The component uses inline styles by default, which you can fully customize using the customStyles prop:
import {
Autocomplete,
type AutocompleteStyles,
} from "react-search-select-dropdown";
import type { AutocompleteOption } from "react-search-select-dropdown";
import "react-search-select-dropdown/styles.css";
const customStyles: AutocompleteStyles = {
input: {
borderColor: "#10b981",
fontSize: "16px",
},
inputFocus: {
borderColor: "#059669",
boxShadow: "0 0 0 3px rgba(16, 185, 129, 0.1)",
},
dropdown: {
borderColor: "#10b981",
boxShadow: "0 10px 25px rgba(0, 0, 0, 0.15)",
},
option: {
padding: "12px 16px",
fontSize: "15px",
},
optionHighlighted: {
backgroundColor: "#d1fae5",
},
optionSelected: {
backgroundColor: "#10b981",
color: "white",
},
};
function App() {
return (
<Autocomplete
options={options}
placeholder="Search..."
customStyles={customStyles}
/>
);
}Available Style Properties
wrapper- Main containerinputWrapper- Input containerinput- Input field base stylesinputFocus- Input field when focusedinputDisabled- Input field when disabledclearButton- Clear button base stylesclearButtonHover- Clear button on hoverarrow- Dropdown arrow base stylesarrowOpen- Dropdown arrow when opendropdown- Dropdown list containeroption- Individual option base stylesoptionHighlighted- Option when highlightedoptionSelected- Option when selectedoptionHighlightedSelected- Option when both highlighted and selectednoOptions- No results message
Custom No Results Text
<Autocomplete options={options} noResultsText="No se encontraron resultados" />Custom Input with renderInput
You can pass a completely custom input component using the renderInput prop. This is useful for custom styling or integrating with UI libraries:
import { Autocomplete, type AutocompleteInputProps } from "react-search-select-dropdown";
import type { AutocompleteOption } from "react-search-select-dropdown";
import "react-search-select-dropdown/styles.css";
const countryOptions: AutocompleteOption[] = [
{ value: "us", label: "United States" },
{ value: "ca", label: "Canada" },
{ value: "mx", label: "Mexico" },
];
function App() {
return (
<Autocomplete
options={countryOptions}
placeholder="Custom styled input..."
onSelect={(option) => {
console.log("Custom input selected:", option);
}}
renderInput={(props: AutocompleteInputProps) => (
<input
ref={props.ref}
type={props.type}
value={props.value}
onChange={props.onChange}
onFocus={props.onFocus}
onBlur={props.onBlur}
onKeyDown={props.onKeyDown}
placeholder={props.placeholder}
disabled={props.disabled}
autoComplete={props.autoComplete}
style={{
...props.style,
borderWidth: "2px",
borderStyle: "solid",
borderColor: "#10b981",
borderRadius: "8px",
fontSize: "16px",
fontWeight: "bold",
padding: "12px 60px 12px 16px",
backgroundColor: "#f0fdf4",
}}
/>
)}
customStyles={{
dropdown: {
maxHeight: "300px",
borderColor: "#10b981",
},
option: {
padding: "12px 16px",
fontSize: "16px",
},
optionHighlighted: {
backgroundColor: "#d1fae5",
},
optionSelected: {
backgroundColor: "#10b981",
color: "white",
},
}}
/>
);
}Custom Input with Material-UI
You can use any UI library component as the input by using the renderInput prop:
import { useState } from "react";
import { Autocomplete, AutocompleteInputProps } from "react-search-select-dropdown";
import type { AutocompleteOption } from "react-search-select-dropdown";
import TextField from "@mui/material/TextField";
import "react-search-select-dropdown/styles.css";
const options: AutocompleteOption[] = [
{ value: "us", label: "United States" },
{ value: "ca", label: "Canada" },
{ value: "mx", label: "Mexico" },
];
function App() {
const [selected, setSelected] = useState<AutocompleteOption | null>(null);
return (
<Autocomplete
options={options}
placeholder="Search country..."
onSelect={setSelected}
renderInput={(props: AutocompleteInputProps) => (
<TextField
inputRef={props.ref}
type={props.type}
value={props.value}
onChange={props.onChange}
onFocus={props.onFocus}
onBlur={props.onBlur}
onKeyDown={props.onKeyDown}
placeholder={props.placeholder}
disabled={props.disabled}
autoComplete={props.autoComplete}
variant="outlined"
fullWidth
label="Country"
size="small"
/>
)}
customStyles={{
inputWrapper: {
position: "relative",
},
dropdown: {
maxHeight: "300px",
},
}}
/>
);
}Complete Example with Multiple Instances
import { useState } from "react";
import { Autocomplete, AutocompleteInputProps } from "react-search-select-dropdown";
import type { AutocompleteOption } from "react-search-select-dropdown";
import "react-search-select-dropdown/styles.css";
interface User {
id: number;
email: string;
role: string;
}
const countryOptions: AutocompleteOption[] = [
{ value: "us", label: "United States" },
{ value: "ca", label: "Canada" },
{ value: "mx", label: "Mexico" },
{ value: "gb", label: "United Kingdom" },
{ value: "fr", label: "France" },
{ value: "de", label: "Germany" },
{ value: "it", label: "Italy" },
{ value: "es", label: "Spain" },
];
const numberOptions: AutocompleteOption<number>[] = [
{ value: 1, label: "Option One" },
{ value: 2, label: "Option Two" },
{ value: 3, label: "Option Three" },
{ value: 4, label: "Option Four" },
{ value: 5, label: "Option Five" },
];
const userOptions: AutocompleteOption<User>[] = [
{
value: { id: 1, email: "[email protected]", role: "admin" },
label: "John Doe (Admin)",
},
{
value: { id: 2, email: "[email protected]", role: "user" },
label: "Jane Smith (User)",
},
{
value: { id: 3, email: "[email protected]", role: "moderator" },
label: "Bob Johnson (Moderator)",
},
{
value: { id: 4, email: "[email protected]", role: "user" },
label: "Alice Williams (User)",
},
];
function App() {
const [selectedCountry, setSelectedCountry] =
useState<AutocompleteOption | null>(null);
const [selectedNumber, setSelectedNumber] =
useState<AutocompleteOption<number> | null>(null);
const [selectedUser, setSelectedUser] =
useState<AutocompleteOption<User> | null>(null);
return (
<div style={{ padding: "40px", maxWidth: "800px", margin: "0 auto" }}>
<h1>React Search Select Dropdown - Examples</h1>
{/* Example 1: String Values */}
<div style={{ marginBottom: "30px" }}>
<label style={{ display: "block", marginBottom: "8px", fontWeight: 500 }}>
Example 1: String Values (Countries)
</label>
<Autocomplete
options={countryOptions}
placeholder="Search country..."
onSelect={setSelectedCountry}
customStyles={{
input: {
borderColor: "gray",
},
dropdown: {
maxHeight: "300px",
},
option: {
padding: "8px 16px",
fontSize: "16px",
},
}}
/>
{selectedCountry && (
<div
style={{
marginTop: "12px",
padding: "12px",
backgroundColor: "#f0fdf4",
border: "1px solid #86efac",
borderRadius: "6px",
color: "#166534",
fontSize: "14px",
lineHeight: "1.6",
}}
>
<strong>Selected:</strong> {selectedCountry.label} (code:{" "}
{selectedCountry.value})
</div>
)}
</div>
{/* Example 2: Number Values */}
<div style={{ marginBottom: "30px" }}>
<label style={{ display: "block", marginBottom: "8px", fontWeight: 500 }}>
Example 2: Number Values
</label>
<Autocomplete
options={numberOptions}
placeholder="Search option..."
onSelect={setSelectedNumber}
customStyles={{
input: {
borderColor: "gray",
},
dropdown: {
maxHeight: "300px",
},
option: {
padding: "8px 16px",
fontSize: "16px",
},
}}
/>
{selectedNumber && (
<div
style={{
marginTop: "12px",
padding: "12px",
backgroundColor: "#f0fdf4",
border: "1px solid #86efac",
borderRadius: "6px",
color: "#166534",
fontSize: "14px",
lineHeight: "1.6",
}}
>
<strong>Selected:</strong> {selectedNumber.label} (ID:{" "}
{selectedNumber.value})
</div>
)}
</div>
{/* Example 3: Custom Object Values */}
<div style={{ marginBottom: "30px" }}>
<label style={{ display: "block", marginBottom: "8px", fontWeight: 500 }}>
Example 3: Custom Object Values (Users)
</label>
<Autocomplete
options={userOptions}
placeholder="Search user..."
getOptionKey={(option) => option.value.id}
onSelect={setSelectedUser}
customStyles={{
input: {
borderColor: "gray",
},
dropdown: {
maxHeight: "300px",
},
option: {
padding: "8px 16px",
fontSize: "16px",
},
}}
/>
{selectedUser && (
<div
style={{
marginTop: "12px",
padding: "12px",
backgroundColor: "#f0fdf4",
border: "1px solid #86efac",
borderRadius: "6px",
color: "#166534",
fontSize: "14px",
lineHeight: "1.6",
}}
>
<strong>Selected User:</strong>
<br />
Name: {selectedUser.label}
<br />
ID: {selectedUser.value.id}
<br />
Email: {selectedUser.value.email}
<br />
Role: {selectedUser.value.role}
</div>
)}
</div>
{/* Example 4: Disabled State */}
<div style={{ marginBottom: "30px" }}>
<label style={{ display: "block", marginBottom: "8px", fontWeight: 500 }}>
Example 4: Disabled State
</label>
<Autocomplete
options={countryOptions}
placeholder="This is disabled..."
disabled={true}
customStyles={{
input: {
borderColor: "gray",
},
dropdown: {
maxHeight: "300px",
},
option: {
padding: "8px 16px",
fontSize: "16px",
},
}}
/>
</div>
{/* Example 5: Custom Input with renderInput */}
<div style={{ marginBottom: "30px" }}>
<label style={{ display: "block", marginBottom: "8px", fontWeight: 500 }}>
Example 5: Custom Input (renderInput)
</label>
<Autocomplete
options={countryOptions}
placeholder="Custom styled input..."
onSelect={(option) => {
console.log("Custom input selected:", option);
}}
renderInput={(props: AutocompleteInputProps) => (
<input
ref={props.ref}
type={props.type}
value={props.value}
onChange={props.onChange}
onFocus={props.onFocus}
onBlur={props.onBlur}
onKeyDown={props.onKeyDown}
placeholder={props.placeholder}
disabled={props.disabled}
autoComplete={props.autoComplete}
style={{
...props.style,
borderWidth: "2px",
borderStyle: "solid",
borderColor: "#10b981",
borderRadius: "8px",
fontSize: "16px",
fontWeight: "bold",
padding: "12px 60px 12px 16px",
backgroundColor: "#f0fdf4",
}}
/>
)}
customStyles={{
dropdown: {
maxHeight: "300px",
borderColor: "#10b981",
},
option: {
padding: "12px 16px",
fontSize: "16px",
},
optionHighlighted: {
backgroundColor: "#d1fae5",
},
optionSelected: {
backgroundColor: "#10b981",
color: "white",
},
}}
/>
</div>
</div>
);
}
export default App;Keyboard Navigation
- Arrow Down: Navigate to next option
- Arrow Up: Navigate to previous option
- Enter: Select highlighted option
- Escape: Close dropdown and blur input
API Reference
Props
| Prop | Type | Default | Description |
| --------------- | ----------------------------------------------------- | ----------------------- | ------------------------------------------------------- |
| options | AutocompleteOption<T>[] | required | Array of options to display |
| placeholder | string | 'Search...' | Placeholder text for the input |
| onSelect | (option: AutocompleteOption<T> \| null) => void | undefined | Callback when an option is selected |
| disabled | boolean | false | Disable the autocomplete |
| getOptionKey | (option: AutocompleteOption<T>) => string \| number | Uses value or label | Function to generate unique keys for options |
| customStyles | AutocompleteStyles | {} | Override default styles with custom inline styles |
| noResultsText | string | 'No results found' | Text to display when no options match the search |
| value | AutocompleteOption<T> \| null | undefined | Controlled value for the autocomplete |
| renderInput | (props: AutocompleteInputProps) => ReactNode | undefined | Custom input component renderer for maximum flexibility |
Types
interface AutocompleteOption<T = string> {
value: T;
label: string;
}
interface AutocompleteInputProps {
ref: React.RefObject<HTMLInputElement>;
type: string;
style: CSSProperties;
placeholder: string;
value: string;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
onFocus: () => void;
onBlur: () => void;
onKeyDown: (e: KeyboardEvent<HTMLInputElement>) => void;
disabled: boolean;
autoComplete: string;
}
interface AutocompleteStyles {
wrapper?: CSSProperties;
inputWrapper?: CSSProperties;
input?: CSSProperties;
inputFocus?: CSSProperties;
inputDisabled?: CSSProperties;
clearButton?: CSSProperties;
clearButtonHover?: CSSProperties;
arrow?: CSSProperties;
arrowOpen?: CSSProperties;
dropdown?: CSSProperties;
option?: CSSProperties;
optionHighlighted?: CSSProperties;
optionSelected?: CSSProperties;
optionHighlightedSelected?: CSSProperties;
noOptions?: CSSProperties;
}
interface AutocompleteProps<T = string> {
options: AutocompleteOption<T>[];
placeholder?: string;
onSelect?: (option: AutocompleteOption<T> | null) => void;
disabled?: boolean;
getOptionKey?: (option: AutocompleteOption<T>) => string | number;
customStyles?: AutocompleteStyles;
noResultsText?: string;
value?: AutocompleteOption<T> | null;
renderInput?: (props: AutocompleteInputProps) => ReactNode;
}License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
