Ownership Violations
Inputs must have a single ownership model for their whole lifetime. Mixing value and defaultValue breaks that invariant and produces warnings, subtle bugs, and confusing UX.
Break the invariant: ownership violation
Minimal interaction first. Flip ownership once and observe how the system state changes. Then read the explanation and compare correct vs buggy code.
Do this once: toggle Make it controlled. The page will simulate the missing value automatically.
Input under test
Input
Why this breaks
The invariant is: single ownership for lifetime. Controlled inputs must always receive a defined
value. If the value becomes undefined, React may treat the input as uncontrolled. Once the same DOM node flips ownership, behavior becomes non-deterministic.Detected issue
Status: READY
Mode changed: — · Flips: 0
Toggle once to reproduce.
Reference (correct mental model)
Uncontrolled (correct)
<input defaultValue="seed" />Controlled (correct)
Controlled (correct): value must never be undefined
<input value={value ?? ""} onChange={...} />
Fix
No visual preview needed
Buggy
// Buggy (uncontrolled)
<input defaultValue="seed" value={value} />Correct
// Correct (uncontrolled)
<input defaultValue="seed" />Key takeaways
- The invariant: single ownership for lifetime (controlled or uncontrolled).
- Controlled inputs must keep
valuedefined (oftenvalue ?? ""). - Uncontrolled inputs use
defaultValue; don’t mix it withvalueon the same DOM node.
Approach & tradeoffs
Approach
- Decide ownership first: controlled vs uncontrolled.
- Enforce invariants: controlled = always string; uncontrolled = defaultValue only.
- Keep rerenders cheap if you choose controlled (avoid large trees rerendering per keystroke).
Tradeoffs
- Controlled inputs can cause frequent rerenders; optimize expensive subtrees.
- Uncontrolled inputs reduce React overhead but may need refs for reads on submit.
- Switching ownership later is costly — treat it like an API decision.
Common pitfall