React Select Dropdowns
Table of Contents + −
In the last lesson you learned about React Textarea, so you can already control a text box with value and onChange. This lesson handles a <select> dropdown the React way, because it behaves a bit differently than it does in plain HTML.
🤔 Why does a select need special handling?
In plain HTML, a dropdown keeps track of its own choice. React wants to be the one in charge instead.
- In HTML, you mark the chosen option with the
selectedattribute, and the browser remembers the pick for you. - In React, you don’t want the browser holding that value on its own. You want it in state, where your code can read it and react to it.
- So React flips it around. The
<select>shows whatever your state says, and every change goes back into state first. - That two-way tie between state and the box is what we call a controlled component.
📋 A controlled select
A controlled select has two parts. You put value on the <select> tag, and you give it an onChange to catch every pick.
Here a small component lets the user choose a fruit, and the choice lives in state.
import { useState } from "react";
function FruitPicker() { const [fruit, setFruit] = useState("apple");
return ( <select value={fruit} onChange={(e) => setFruit(e.target.value)}> <option value="apple">Apple</option> <option value="banana">Banana</option> <option value="cherry">Cherry</option> </select> );}Let’s walk through what makes this controlled.
value={fruit}on the<select>tells the dropdown which option to show. It always matches our state.onChangeruns the moment the user picks a different option.- Inside it,
setFruitsaves the new pick, so state stays the single source of truth. - Notice the chosen option is decided by the
<select>, not by anyselectedon an<option>.
⚠️ value on the select, not selected on the option
This is the one big difference from HTML, so let’s see it clearly. In HTML you mark the option. In React you set the value on the select itself.
Here’s the old HTML habit next to the React way.
// ❌ Wrong - HTML style, putting selected on an option<select> <option value="apple">Apple</option> <option value="banana" selected>Banana</option></select>
// ✅ Right - React style, value on the select plus onChange<select value={fruit} onChange={(e) => setFruit(e.target.value)}> <option value="apple">Apple</option> <option value="banana">Banana</option></select>Here’s why the React way wins.
- With
selectedon an option, the browser owns the choice and React loses track of it. - With
valueon the<select>, your state decides the choice, so your code always knows the current pick. - Change the state and the dropdown updates by itself. That’s the whole point of controlling it.
selected does nothing useful here
If you put selected on an <option> in React, you’ll get a warning and the value won’t stay in sync with your state. Always set the choice with value on the <select> instead.
⌨️ Reading the chosen value
Whenever the user picks an option, React hands your handler an event object. The chosen value sits at e.target.value, the same place you read text inputs from.
Here we read the pick and use it to show a small message.
import { useState } from "react";
function FruitPicker() { const [fruit, setFruit] = useState("apple");
function handleChange(e) { setFruit(e.target.value); }
return ( <div> <select value={fruit} onChange={handleChange}> <option value="apple">Apple</option> <option value="banana">Banana</option> <option value="cherry">Cherry</option> </select> <p>You picked: {fruit}</p> </div> );}Here’s the flow in order.
- The user picks an option, so
onChangefires. e.targetis the<select>element, ande.target.valueis thevalueof the option they chose.setFruitsaves that value into state.- React re-renders, the dropdown shows the new pick, and the
<p>updates too.
Output
(user picks "Banana")
You picked: banana🗺️ Rendering options from an array
Typing each <option> by hand is fine for three choices. But real lists, like countries or cities, come from data. So you build the options with map, the same way you’d render any list.
Here we keep the choices in an array and turn each one into an option.
import { useState } from "react";
function CityPicker() { const cities = ["London", "Tokyo", "Mumbai", "Berlin"]; const [city, setCity] = useState("London");
return ( <select value={city} onChange={(e) => setCity(e.target.value)}> {cities.map((name) => ( <option key={name} value={name}> {name} </option> ))} </select> );}A few things to notice in that map.
cities.map(...)walks the array and gives back one<option>per item.- Each option needs a
key, just like any mapped list, so React can tell them apart. - We set both
value={name}and the text inside, so the saved value matches what the user sees. - The
<select>still controls the choice withvalue={city}. The data only fills in the options.
🏷️ Adding a placeholder option
Often you want the dropdown to start on something like “Choose a city” instead of a real pick. You add a disabled first option and start your state empty.
Here the first option acts as a placeholder that can’t be re-selected.
function CityPicker() { const cities = ["London", "Tokyo", "Mumbai"]; const [city, setCity] = useState("");
return ( <select value={city} onChange={(e) => setCity(e.target.value)}> <option value="" disabled> Choose a city </option> {cities.map((name) => ( <option key={name} value={name}> {name} </option> ))} </select> );}Here’s how the placeholder works.
- State starts as
"", so the empty-valued option shows first. - That first option has
disabled, so once the user picks a real city, they can’t go back to the prompt. - The moment they pick a city, state updates and the dropdown shows their choice.
Match the empty value
For the placeholder to show at the start, the option’s value must match your starting state. An empty string in both, value="" and useState(""), is the easy way to line them up.
🔢 A quick word on multiple select
A dropdown can also let the user pick more than one option at once. You turn it on with the multiple attribute, and then the value becomes an array.
Here’s the shape of a multi-select, just so you’ve seen it.
const [picked, setPicked] = useState([]);
<select multiple value={picked} onChange={(e) => setPicked(Array.from(e.target.selectedOptions, (opt) => opt.value)) }> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option></select>;The key differences to remember.
- With
multiple, the state holds an array of values, not a single string. - You can’t just read
e.target.valueanymore. You reade.target.selectedOptionsand pull each value out. - For most forms a single-choice dropdown is what you need, so reach for multi-select only when the task really calls for it.
🧩 What You’ve Learned
- ✅ A
<select>in React is a controlled component, with the choice held in state - ✅ You set the chosen option with
valueon the<select>, notselectedon an<option> - ✅
onChangefires when the user picks, and the choice is ate.target.value - ✅ Save that value into state with a setter, so state and the dropdown stay in sync
- ✅ Build options from an array with
map, giving each<option>akey - ✅ A disabled empty-valued option acts as a placeholder when state starts as
"" - ✅ A
multipleselect holds an array of values, read frome.target.selectedOptions
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
In React, how do you set which option is chosen in a select?
Why: A controlled select sets its choice with value on the <select> itself, plus an onChange to update state. The selected attribute is the HTML way and does not stay in sync with React state.
- 2
Inside the select's onChange, how do you read the option the user picked?
Why: For a single-choice select, e.target is the select element and e.target.value is the value of the chosen option. You usually save it into state with a setter.
- 3
When you render <option> elements from an array with map, what must each option have?
Why: Like any mapped list in React, each <option> needs a unique key prop so React can tell the items apart when the list changes.
- 4
How do you make the first option act as a placeholder like 'Choose a city'?
Why: A first <option> with value empty and disabled shows as the prompt when state starts as empty. Because it is disabled, the user can't return to it after picking a real choice.
🚀 What’s Next?
Now you can handle a dropdown the React way, control it with value, and read the pick from e.target.value. Next we’ll look at another way to let users choose, this time picking one option from a small set with radio buttons.