React warns when a select element is provided with a value or uses the 'selected' attribute without an onChange handler. This creates a controlled component that cannot be updated, rendering it read-only unintentionally.
This warning occurs when you attempt to control a <select> element's value without providing a way to update it. React distinguishes between controlled components (where React manages the value via state and onChange handlers) and uncontrolled components (where the DOM manages its own state). The warning specifically appears in two scenarios: when you use a 'value' prop on a <select> element without an 'onChange' handler, or when you use the HTML 'selected' attribute directly on <option> elements (which React doesn't support). In both cases, React cannot update the selection, making the dropdown effectively read-only even though it appears interactive. This is React's way of preventing a common mistake: creating a form field that looks interactive but cannot actually be changed by the user. The warning helps developers choose the correct pattern—either fully controlled (with onChange) or uncontrolled (with defaultValue).
If you want React to control the select value, provide both 'value' and 'onChange' props:
import { useState } from 'react';
function MyDropdown() {
const [selectedValue, setSelectedValue] = useState('option1');
const handleChange = (e) => {
setSelectedValue(e.target.value);
};
return (
<select value={selectedValue} onChange={handleChange}>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
);
}The 'value' prop on <select> determines which option is selected, not the 'selected' attribute on <option>.
If you want the DOM to manage the select value, use 'defaultValue' instead of 'value':
function MyDropdown() {
return (
<select defaultValue="option2">
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
);
}With 'defaultValue', React sets the initial selection but doesn't control subsequent changes.
If the select should be displayed but not editable, add the 'readOnly' prop:
function MyDropdown() {
return (
<select value="option1" readOnly>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
);
}Note: 'readOnly' isn't fully supported on <select> elements in all browsers. Consider using 'disabled' instead, or handle clicks to prevent changes.
React doesn't support the 'selected' attribute on individual options. This is incorrect:
// ❌ Wrong - causes warning
<select>
<option value="option1">Option 1</option>
<option value="option2" selected>Option 2</option>
<option value="option3">Option 3</option>
</select>Instead, pass the selected value to the parent <select> element:
// ✅ Correct - controlled
<select value="option2" onChange={handleChange}>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
// ✅ Correct - uncontrolled
<select defaultValue="option2">
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>Controlled vs Uncontrolled Components
React provides two patterns for form elements:
1. Controlled: React state is the "single source of truth". Use 'value' + 'onChange' props. Every state mutation has an associated handler function, making it easier to validate or modify user input.
2. Uncontrolled: The DOM maintains its own internal state. Use 'defaultValue' prop for initial value. Access current value via refs or form submission. Simpler for basic forms but harder to validate in real-time.
Why React Uses 'value' on <select>
In HTML, the 'selected' attribute on <option> elements determines which option is selected. React simplified this by using a 'value' attribute on the root <select> tag instead, making it consistent with other form elements like <input> and <textarea>.
Multiple Select Elements
For <select multiple>, the 'value' prop accepts an array:
const [selected, setSelected] = useState(['option1', 'option3']);
<select
multiple
value={selected}
onChange={(e) => {
const values = Array.from(e.target.selectedOptions, opt => opt.value);
setSelected(values);
}}
>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>Performance Considerations
For large dropdown lists (100+ options), consider:
- Virtualization libraries like react-window
- Third-party libraries like react-select with async loading
- Native <datalist> elements for simple autocomplete scenarios
React.FC expects children prop to be defined
React.FC no longer includes implicit children prop
Failed to load source map from suspense chunk
How to fix "Failed to load source map from suspense chunk" in React
Prop spreading could cause security issues
Prop spreading could cause security issues
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support