How to fix A component is changing a controlled input to be uncontrolled in React

ReactINTERMEDIATEMEDIUMUpdated Dec 8, 2025· 117 views

This warning occurs when an input element switches from being controlled by React state to being uncontrolled, typically when the value prop changes from a defined value to undefined or null. The fix involves ensuring consistent state initialization and preventing undefined values from being passed to controlled inputs.

Quick fix

Fixes A component is changing a controlled input to be uncontrolled

// ❌ Bad - starts as undefined
const [name, setName] = useState();

// ❌ Also bad - explicitly undefined
const [email, setEmail] = useState(undefined);

// ✅ Good - starts as empty string
const [name, setName] = useState('');

// ✅ Also good - explicit empty string
const [email, setEmail] = useState<string>('');
  1. Initialize state with empty string instead of undefined
  2. Use nullish coalescing for potentially undefined values
  3. Handle async data loading with proper initialization
  4. Convert undefined values in onChange handlers
react — error
// ❌ Bad - starts as undefined
const [name, setName] = useState();
// ❌ Also bad - explicitly undefined
const [email, setEmail] = useState(undefined);
// ✅ Good - starts as empty string
const [name, setName] = useState('');
// ✅ Also good - explicit empty string
const [email, setEmail] = useState<string>('');
A component is changing a controlled input to be uncontrolled

What this error means

This warning appears when React detects that an input element has switched from being controlled (where its value is managed by React state) to being uncontrolled (where the DOM manages its own state). React requires that an input element remain either controlled or uncontrolled throughout its entire lifetime. A controlled input is one where you pass a value prop that is bound to React state, and typically also provide an onChange handler to update that state. When the value prop transitions from a defined value (like a string) to undefined or null, React interprets this as the input becoming uncontrolled, which triggers this warning. This issue most commonly occurs when state is initialized as undefined, when data is being loaded asynchronously and hasn't arrived yet, or when optional form fields conditionally receive undefined values. React is strict about this because switching between paradigms can lead to unpredictable behavior and loss of user input.

Typical symptoms

  • Console warning: "A component is changing a controlled input to be uncontrolled"
  • Input field loses its value when component re-renders
  • User typing is interrupted or input behaves erratically
  • Form values reset unexpectedly during state updates
  • Input field appears to work initially but breaks after data loads
  • Warning occurs specifically when async data arrives or state updates
  • Multiple warnings appear if multiple form fields are affected

Common causes

  1. State variable initialized with undefined instead of an empty string: useState() or useState(undefined)
  2. Value prop receives undefined from props or API data that hasn't loaded yet
  3. Conditional rendering passes undefined when optional data is missing
  4. Object destructuring assigns undefined when expected property doesn't exist
  5. State reset logic sets value to undefined instead of empty string
  6. Form field mapped from array where some items lack the required property
  7. Ternary operator returns undefined in one branch: value={condition ? data : undefined}
  8. API response properties accessed before data is available

How to fix "A component is changing a controlled input to be uncontrolled"

1Initialize state with empty string instead of undefined

The most common fix is to ensure your state is initialized with an empty string, not undefined:

tsx
// ❌ Bad - starts as undefined
const [name, setName] = useState();

// ❌ Also bad - explicitly undefined
const [email, setEmail] = useState(undefined);

// ✅ Good - starts as empty string
const [name, setName] = useState('');

// ✅ Also good - explicit empty string
const [email, setEmail] = useState<string>('');

This ensures the input is controlled from the very first render, preventing the controlled-to-uncontrolled switch.

2Use nullish coalescing for potentially undefined values

When your value might be undefined (from props or API data), provide a fallback:

tsx
// ❌ Bad - value can be undefined
<input value={userData?.name} onChange={handleChange} />

// ✅ Good - always provides a string
<input value={userData?.name ?? ''} onChange={handleChange} />

// Also works with logical OR
<input value={userData?.name || ''} onChange={handleChange} />

The nullish coalescing operator (??) or logical OR (||) ensures the value prop never receives undefined.

3Handle async data loading with proper initialization

When loading data from an API, initialize your form state before data arrives:

tsx
function UserForm() {
  // ❌ Bad - starts undefined until data loads
  const [name, setName] = useState(userData?.name);

  // ✅ Good - starts as empty, updates when data arrives
  const [name, setName] = useState('');

  useEffect(() => {
    if (userData?.name) {
      setName(userData.name);
    }
  }, [userData]);

  return <input value={name} onChange={(e) => setName(e.target.value)} />;
}

This pattern ensures the input remains controlled even before data loads.

4Convert undefined values in onChange handlers

If you must work with potentially undefined values, sanitize them in your handler:

tsx
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
  const newValue = e.target.value;
  // Ensure we never set undefined
  setName(newValue ?? '');
}

// Or more robustly
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
  setName(e.target.value || '');
}

This defensive approach prevents undefined from ever entering your state.

5Use defaultValue for truly uncontrolled inputs

If you don't need to control the input with React state, make it fully uncontrolled:

tsx
// ❌ Bad - mixing controlled and uncontrolled
<input value={undefined} />

// ✅ Good - fully uncontrolled with defaultValue
<input defaultValue="" ref={inputRef} />

// Access value via ref when needed
function handleSubmit() {
  const value = inputRef.current?.value;
}

Use defaultValue instead of value, and access the current value via a ref. This is appropriate for simple forms where you don't need real-time validation.

6Fix mapped form fields with consistent fallbacks

When rendering form fields from an array, ensure each has a valid value:

tsx
// ❌ Bad - some items might not have the property
{fields.map(field => (
  <input
    key={field.id}
    value={field.value}
    onChange={(e) => handleFieldChange(field.id, e.target.value)}
  />
))}

// ✅ Good - guarantee every input has a string value
{fields.map(field => (
  <input
    key={field.id}
    value={field.value ?? ''}
    onChange={(e) => handleFieldChange(field.id, e.target.value)}
  />
))}

This pattern is especially important when working with dynamic forms or field arrays.

7Review form reset logic

Ensure your form reset or clear functions don't introduce undefined values:

tsx
// ❌ Bad - resets to undefined
function resetForm() {
  setName(undefined);
  setEmail(undefined);
}

// ✅ Good - resets to empty strings
function resetForm() {
  setName('');
  setEmail('');
}

// Or use an initial state constant
const INITIAL_STATE = { name: '', email: '' };
function resetForm() {
  setFormData(INITIAL_STATE);
}

Always reset controlled inputs to empty strings, not undefined.

Advanced notes

TypeScript users: Enable strict null checks in your tsconfig.json to catch undefined values at compile time. Type your state explicitly as useState<string>('') rather than relying on inference.

Performance consideration: If you have many form fields and are concerned about re-renders, consider using uncontrolled components with refs and accessing values only on submit. This trades off real-time validation for better performance.

React 19 changes: React 19 has improved handling of this scenario and provides clearer error messages. However, the fundamental rule remains: an input must be consistently controlled or uncontrolled throughout its lifetime.

Form libraries: Libraries like React Hook Form and Formik handle this complexity for you by ensuring all registered fields maintain controlled state consistently. Consider using them for complex forms.

Select and textarea elements: This same warning applies to <select> and <textarea> elements. The fixes are identical - ensure their value props never transition from defined to undefined.

Testing: When writing tests, ensure you initialize component state properly. Test utilities like React Testing Library's userEvent will respect controlled input behavior.

Common Redux pattern: When using Redux or other state management, ensure your selectors return empty strings for missing values rather than undefined: const name = useSelector(state => state.form.name ?? '');

Frequently asked questions

What does "A component is changing a controlled input to be uncontrolled" mean?
This warning appears when React detects that an input element has switched from being controlled (where its value is managed by React state) to being uncontrolled (where the DOM manages its own state). React requires that an input element remain either controlled or uncontrolled throughout its entire lifetime. A controlled input is one where you pass a value prop that is bound to React state, and typically also provide an onChange handler to update that state.
How do I fix "A component is changing a controlled input to be uncontrolled"?
This warning occurs when an input element switches from being controlled by React state to being uncontrolled, typically when the value prop changes from a defined value to undefined or null. The fix involves ensuring consistent state initialization and preventing undefined values from being passed to controlled inputs.
What causes "A component is changing a controlled input to be uncontrolled"?
State variable initialized with undefined instead of an empty string: useState() or useState(undefined) Value prop receives undefined from props or API data that hasn't loaded yet Conditional rendering passes undefined when optional data is missing
Is the "A component is changing a controlled input to be uncontrolled" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading