LEARN · DEBUGGING GUIDE

Diagnosing React's Controlled vs. Uncontrolled Component Warning

React throws a controlled/uncontrolled warning when an input toggles between managed and unmanaged. Here's exactly how to pinpoint and fix the issue.

BeginnerReact bugs4 min read

What this usually means

This warning means a component's input field is flipping between controlled (value prop is set) and uncontrolled (value is undefined, using defaultValue or internal state). This usually stems from the value prop switching between undefined and a legitimate value, often due to initial state being undefined/null, missed state updates, or conditional rendering logic gone wrong.

( 01 )Fast diagnosis

The first ten minutes — establish facts before touching code.

  • 1Check the initial value passed to the input's value prop—is it ever undefined or null?
  • 2Search for all instances of defaultValue and value props in the relevant components.
  • 3Confirm the state variable driving the input value is always initialized (e.g., useState(''), not useState()).
  • 4Examine your conditional rendering—are you rendering the input before state is loaded?
  • 5Temporarily add console.log to the value prop to trace how/when it becomes undefined.
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • searchThe specific input component's JSX (look for value and defaultValue usage)
  • searchRelated useState/useReducer hooks—especially initial state values
  • searchParent component's data-fetching logic (is the state missing until fetch resolves?)
  • searchReact DevTools: Inspect input props during component lifecycle
  • searchBrowser console for the full warning (it usually includes a stack or component name)
  • searchAny code auto-populating form fields (e.g., after an API call or prop change)
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warningInitializing state to undefined or null rather than '' or []
  • warningSwitching from defaultValue to value in the same input
  • warningConditionally rendering value on async data that starts as undefined
  • warningIncorrect or missing value prop when mapping over dynamic form fields
  • warningForgetting to handle onChange for controlled components, so value never updates
  • warningCopy-pasting code that mixes controlled and uncontrolled pattern
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildAlways initialize useState variables for inputs as '' (for text), [] (for arrays), or {} (for objects)
  • buildEnsure you never set value={undefined} or value={null}; coerce with value={val || ''}
  • buildPick one: use value/onChange for controlled, or defaultValue for uncontrolled, never both
  • buildDelay rendering inputs until data is available, or provide a fallback value
  • buildAudit dynamic forms to guarantee each input has a stable key and value
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedCheck the browser console—warning should be gone after reload
  • verifiedInputs should no longer clear or ignore edits unexpectedly
  • verifiedForm state in React DevTools should always show defined values for all inputs
  • verifiedNo more switching between undefined/null and real values when inspecting input props
  • verifiedRe-run all form field flows with slow network to ensure initial loading is handled
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningUsing value={someVar} when someVar can ever be undefined or null
  • warningMixing defaultValue and value on the same input component
  • warningRelying on fetched/async data to initialize a controlled input without fallback
  • warningLeaving out the onChange handler for an input with value set
  • warningBlindly suppressing the warning without addressing the root case
( 07 )War story

Sudden Resetting Inputs After Adding Async Data Fetch

Frontend EngineerReact 17, functional components, Axios, custom hooks

Timeline

  1. 09:00Deployed new user registration form to staging
  2. 09:03QA reports name and email fields clear after each keystroke
  3. 09:08Console warning: 'A component is changing an uncontrolled input to be controlled...'
  4. 09:10Noticed useState fields were initialized to undefined
  5. 09:15Changed useState(undefined) to useState('')
  6. 09:18Warning disappeared, inputs behave normally
  7. 09:22Root cause identified: missing initial value caused uncontrolled/controlled flip

I was rolling out a new sign-up form where some fields were prefilled by an API call. I used useState() with no initial value and set the state inside a useEffect after fetching data.

Immediately, QA pinged me that name and email fields were resetting on every keystroke. The browser console threw the classic warning about controlled/uncontrolled inputs.

I realized the input was uncontrolled (no value) before the API loaded, then switched to controlled once the fetch completed. Setting useState('') from the start fixed the issue. I learned to always initialize controlled input state for consistent behavior.

Root cause

useState(undefined) left input uncontrolled until API data loaded, causing React to complain and fields to reset.

The fix

Set useState('') for all input fields and ensure value props are always non-undefined.

The lesson

Always initialize input state; never let value or checked props be undefined or null in controlled inputs.

( 08 )Why React Warns About Controlled/Uncontrolled Inputs

React enforces a clear distinction: either you control the value of an input via state and value/onChange, or you let the DOM manage its value (uncontrolled, using defaultValue). Flipping between the two at runtime breaks the internal contract for reconciliation, causing unpredictable UI and data bugs.

A controlled input must always receive a value prop. Undefined/null means uncontrolled. If the value prop appears only after an async fetch, the input switches from uncontrolled to controlled, and React flags this as a likely bug.

( 09 )Input Initialization Patterns That Break

Leaving useState() or useState(null) as your initial value is a silent trap. Inputs bind to value={stateVar}, so on the first render, React sees value={undefined} (uncontrolled), then later value={'foo'} (controlled).

This is especially common if you hydrate form state with data fetched in useEffect, or if you map dynamic keys to form fields but forget to fill in every initial value.

( 10 )Best Practices to Prevent This Warning Permanently

Always initialize text, email, or textarea input values to an empty string: useState('') or useReducer with a default object. For checkboxes or radios, use false or true as appropriate.

When working with forms backed by async data, either delay rendering the form until data is loaded, or provide a fallback like value={user.name || ''} to guarantee a defined value at all times.

Frequently asked questions

What exactly triggers the controlled/uncontrolled warning in React?

React shows this warning if an input's value prop changes from undefined (uncontrolled) to any defined value (controlled), or vice versa, during component re-renders.

Can I use both value and defaultValue on the same input?

No. Use value with onChange for controlled inputs, or defaultValue for uncontrolled. Mixing both leads to unpredictable state and the warning.

How do I handle inputs with async-loaded initial values?

Either render the input only after data loads, or always provide a fallback: value={dataFromApi || ''}. Never let value be undefined.

What about select, checkbox, or radio inputs?

Same rule: their value or checked props must always be initialized (e.g., checked={checkedState || false}). Never let them be undefined.

Does this warning mean my code is broken?

Often yes—the flip introduces subtle bugs (inputs resetting, ignoring changes). React warns to highlight a real problem that can bite in production.