How to Handle the Selected Attribute for a Select in React with Dynamic Options

Use the value prop (for controlled components) or defaultValue prop (for uncontrolled components) on the <select> element itself, never the selected attribute on individual <option> elements.

When building dynamic forms in React, managing the selected attribute for a select in React requires understanding how the library internally synchronizes state with the DOM. According to the facebook/react source code, React intercepts the native selected property and manages it through specialized binding logic in ReactDOMSelect.js.

Why React Controls the Selected State

React does not allow direct manipulation of the selected attribute on <option> elements. Instead, the framework uses the updateOptions function in packages/react-dom-bindings/src/client/ReactDOMSelect.js to synchronize the DOM with your component's state.

When you provide a value prop to <select>, React runs updateSelect on every render (lines 23-25). This function invokes updateOptions, which iterates through the option nodes and sets option.selected = true for values matching your state (lines 90-98).

Controlled Components with Dynamic Options

For dynamic lists fetched from an API or computed from state, use the controlled pattern with the value prop.

function FruitPicker({ options }) {
  const [selected, setSelected] = React.useState('apple');

  return (
    <select
      value={selected}
      onChange={e => setSelected(e.target.value)}
    >
      {options.map(o => (
        <option key={o.id} value={o.id}>
          {o.name}
        </option>
      ))}
    </select>
  );
}

In this pattern, updateOptions (lines 90-98 in ReactDOMSelect.js) automatically handles the native selected property whenever the options array or selected state changes.

Uncontrolled Components with Default Values

When you want the browser to manage the selection state but need to specify an initial value, use defaultValue.

function CountryPicker({ options }) {
  return (
    <select defaultValue="US">
      {options.map(c => (
        <option key={c.code} value={c.code}>
          {c.name}
        </option>
      ))}
    </select>
  );
}

During mounting, initSelect (lines 52-58 in ReactDOMSelect.js) executes updateOptions with setDefaultSelected = true. This ensures the browser's default selected state matches your defaultValue without React taking control of subsequent changes.

Handling Multi-Select with Dynamic Arrays

For <select multiple> elements, pass an array to value or defaultValue.

function MultiTagSelector({ tags }) {
  const [selectedTags, setSelectedTags] = React.useState(['react', 'js']);

  return (
    <select
      multiple
      value={selectedTags}
      onChange={e => {
        const values = Array.from(
          e.target.selectedOptions, 
          o => o.value
        );
        setSelectedTags(values);
      }}
    >
      {tags.map(t => (
        <option key={t} value={t}>
          {t}
        </option>
      ))}
    </select>
  );
}

According to lines 71-84 in ReactDOMSelect.js, updateOptions builds a lookup map of the array values and marks each matching option as selected. This approach works efficiently even when the tags array changes dynamically.

Critical Implementation Rules from ReactDOMSelect.js

Never Mix Value and DefaultValue

The source code explicitly warns against mixing these props. Lines 30-41 in ReactDOMSelect.js trigger a development-mode warning if you specify both value and defaultValue on the same element. Choose one pattern: controlled (value) or uncontrolled (defaultValue).

Do Not Set Selected Manually

Setting the selected attribute directly on an <option> element has no effect in React. The updateOptions function (called during every render cycle) iterates through all options and overrides any manual selected settings based on the current value or defaultValue prop.

Handling the Multiple Prop Change

If you dynamically switch the multiple prop, React detects this change in updateSelect (lines 25-33). The framework either reapplies the default value or clears the selection to ensure DOM consistency when transitioning between single and multi-select modes.

Summary

  • Use value for controlled components where React manages the selection state, or defaultValue for uncontrolled components where the browser manages state.
  • Never use the selected attribute on individual <option> elements; React's updateOptions function in ReactDOMSelect.js controls this internally.
  • Pass arrays to value or defaultValue when using the multiple attribute.
  • Ensure re-renders occur when dynamic option lists change so React can synchronize the DOM via updateSelect.
  • Avoid mixing value and defaultValue props to prevent development warnings.

Frequently Asked Questions

Can I use the selected attribute on individual option elements in React?

No. React explicitly manages the selected property of option elements through the updateOptions function in ReactDOMSelect.js. Setting selected manually on an <option> has no lasting effect because React overrides it during the reconciliation process to match the value or defaultValue prop provided to the parent <select> element.

What happens if I mix value and defaultValue on the same select element?

React will display a development-mode warning and the behavior may be unpredictable. According to lines 30-41 in ReactDOMSelect.js, these props are mutually exclusive. value creates a controlled component where React manages state, while defaultValue creates an uncontrolled component where the browser manages state. You must choose one pattern per component.

How do I handle a select with multiple selection and dynamically generated options?

Use the multiple prop combined with an array passed to the value prop (for controlled) or defaultValue prop (for uncontrolled). When the options array changes dynamically, ensure your component re-renders so React can execute updateOptions (lines 71-84 in ReactDOMSelect.js), which builds a lookup map of your array values and marks each corresponding option as selected.

Why doesn't my select update when the options array changes but the value stays the same?

React's updateSelect function runs on every render, but if your value prop references the same primitive (e.g., a string ID) while the options array changes, the selection should still synchronize correctly. However, if the previously selected value no longer exists in the new options array, the browser will typically show a blank selection. Ensure your state management updates the value when the options change if the current selection becomes invalid, triggering React's updateOptions to clear or reset the selection appropriately.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →