React Select
A controlled select ties a dropdown's chosen option to component state, giving you the same predictable data flow as text inputs and textareas.
The Basic Controlled Select
A <select> renders a list of <option> children, and in React you control which one is selected by setting the select element's value prop to match an option's value — not by putting a 'selected' attribute on the <option> itself, as you would in plain HTML.
A Simple Controlled Select
import { useState } from 'react';
function PlanPicker() {
const [plan, setPlan] = useState('basic');
return (
<select value={plan} onChange={(e) => setPlan(e.target.value)}>
<option value="basic">Basic</option>
<option value="pro">Pro</option>
<option value="enterprise">Enterprise</option>
</select>
);
}
export default PlanPicker;Rendering Options from Data
Hardcoding <option> tags works for small fixed lists, but most real selects are generated from an array, often fetched from an API. Map over the array and give each generated option a stable, unique key.
Generating Options from an Array
import { useState } from 'react';
const countries = [
{ code: 'US', name: 'United States' },
{ code: 'IN', name: 'India' },
{ code: 'DE', name: 'Germany' },
];
function CountrySelect() {
const [country, setCountry] = useState('US');
return (
<select value={country} onChange={(e) => setCountry(e.target.value)}>
{countries.map((c) => (
<option key={c.code} value={c.code}>
{c.name}
</option>
))}
</select>
);
}
export default CountrySelect;Multiple Selection
Adding the multiple attribute turns a select into a multi-choice list. Its value must then be an array of the selected option values rather than a single string, and reading the new selection out of the onChange event requires looping over e.target.selectedOptions (or e.target.options) since there is no single e.target.value to rely on.
A Multi-Select of Tags
import { useState } from 'react';
function TagSelect() {
const [tags, setTags] = useState(['react']);
function handleChange(e) {
const selected = Array.from(
e.target.selectedOptions,
(option) => option.value
);
setTags(selected);
}
return (
<select multiple value={tags} onChange={handleChange}>
<option value="react">React</option>
<option value="vue">Vue</option>
<option value="svelte">Svelte</option>
<option value="angular">Angular</option>
</select>
);
}
export default TagSelect;- Single select: value is a string, onChange reads e.target.value
- Multi select: value is an array, onChange reads e.target.selectedOptions
- optgroup can visually group related <option>s without changing the value logic
- A placeholder option is usually a disabled <option value=""> at the top of the list
Exercise: React Select
How do you turn a <select> element into a controlled component in React?