A controlled input has React as the single source of truth for its current value, via the value prop, updated through an onChange handler that keeps state in sync with every keystroke, letting the component always know, validate, or react to the current value in real time. An uncontrolled input instead lets the DOM manage its own value internally, the way a plain HTML form traditionally works, with React only reading that value on demand via a ref, typically at the moment of form submission rather than on every keystroke — defaultValue, not value, sets an uncontrolled input's initial content without making it controlled.
1Understanding Controlled vs Uncontrolled
A controlled input has React as the single source of truth for its current value, via the value prop, updated through an onChange handler that keeps state in sync with every keystroke, letting the component always know, validate, or react to the current value in real time. An uncontrolled input instead lets the DOM manage its own value internally, the way a plain HTML form traditionally works, with React only reading that value on demand via a ref, typically at the moment of form submission rather than on every keystroke — defaultValue, not value, sets an uncontrolled input's initial content without making it controlled.
Mixing value and defaultValue on the same input, or switching an input between having and not having a value prop across renders, produces confusing behavior and React warnings — commit to either the controlled or uncontrolled pattern for a given input and stick with it consistently.
import { useState } from 'react';
function ControlledInput() {
const [value, setValue] = useState('');
return <input value={value} onChange={(e) => setValue(e.target.value)} />;
}2Practical Example
Here is a real-world application of Controlled vs Uncontrolled showing how it is used in production React code.
import { useRef } from 'react';
function UncontrolledInput() {
const inputRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
console.log('Value at submission:', inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input ref={inputRef} defaultValue="initial" />
<button type="submit">Submit</button>
</form>
);
}3Best Practices
Follow these guidelines when working with Controlled vs Uncontrolled:
1. Use controlled inputs by default, especially when you need real-time validation, conditional UI, or to know the current value before actual form submission
2. Use uncontrolled inputs, reading their value via a ref only at submission time, for simpler forms with no per-keystroke needs, or when integrating with non-React code expecting to manage its own input state
3. Use defaultValue, not value, to set an uncontrolled input's starting content, keeping the two patterns clearly separated rather than mixed on the same element
Tip: Mixing value and defaultValue on the same input, or switching an input between having and not having a value prop across renders, produces confusing behavior and React warnings — commit to either the controlled or uncontrolled pattern for a given input and stick with it consistently.
import { useState } from 'react';
function ControlledInput() {
const [value, setValue] = useState('');
return <input value={value} onChange={(e) => setValue(e.target.value)} />;
}