React complains when an input starts with no value (uncontrolled) and later receives a value prop. Keeping inputs consistently controlled or using defaultValue avoids the warning and keeps form state predictable.
This warning means React detected an input (or checkbox/select) that began without a value or checked prop and then received one on a later render. React treats that as an uncontrolled component switching to a controlled one, which is not allowed because it causes glitches and breaks the single source of truth for the field. The usual culprit is that the value prop is undefined on the first render (often because you skipped the initial state or data hasn't loaded yet), and then becomes a string once the user types or remote data arrives. React expects the prop to remain defined throughout the component's lifetime so it can keep the DOM in sync. If you truly never want React to control the input, use defaultValue/defaultChecked instead. This warning also shows up for checked on radios/checkboxes and for value on selects and textareas. The fix is to keep using the same paradigm (controlled vs uncontrolled) and sanitize any undefined values before passing them to the input.
Set the state you bind to value (or checked for radios/checkboxes) to a concrete value from the start:
const [message, setMessage] = useState('');
const [agreed, setAgreed] = useState(false);Even if the API result arrives later, React never sees undefined as the value, so the warning never fires. If you use TypeScript, type the state explicitly (useState<string>('')).
Guard against undefined responses by falling back to an empty string:
<input value={user?.nickname ?? ''} onChange={(e) => setNickname(e.target.value)} />You can also use logical OR (||) when you truly expect an empty string fallback. This keeps value defined even if the prop is missing or null.
If you fetch data before rendering a form, wait for the payload with an early return:
if (!user) {
return <p>Loading…</p>;
}
return (
<input value={user.email} onChange={(e) => setEmail(e.target.value)} />
);This avoids mounting the input with an undefined value while you still treat it as controlled.
When you do not need to update React state on each keystroke, make the input uncontrolled:
<input defaultValue="initial" ref={inputRef} />Only read the ref.current.value when you need it (e.g., on submit). Do not pair defaultValue with value on the same element, because that mixes paradigms.
If you reset form state, keep the inputs controlled by resetting to empty values:
const RESET_STATE = { message: '', email: '' };
setFormState(RESET_STATE);Avoid resetting fields to undefined/null, which would temporarily drop them from control and trigger the warning again.
When rendering fields from an array or nested object, provide fallbacks per item:
{fields.map((field) => (
<input
key={field.id}
value={field.value ?? ''}
onChange={(e) => updateField(field.id, e.target.value)}
/>
))}This prevents sparse data from passing undefined for some items while others already have values.
TypeScript tip: Enable strict-null-checks and type your form state so the compiler demands an initial string/boolean. React 18+ forms: Hooks and concurrent behavior didn't change the warning; React still enforces consistent control. Other elements: The same guardrails apply to <textarea> and <select>. Always treat checked on radios/checkboxes the same way you treat value on text inputs.
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
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite