Controlled Inputs
Overview
In traditional HTML, input fields (like <input>, <textarea>, <select>) manage their own internal state. When a user types a letter, the browser updates the input's visual text natively.
In React, having HTML control its own state violates the principle that React State should be the Single Source of Truth. If the HTML controls the value, React doesn't know what the user typed, making it difficult to enforce validation, format numbers, or conditionally enable a 'Submit' button.
To fix this, we use the Controlled Input pattern.
1. We force the input's value attribute to equal a React useState variable.
2. We listen to the onChange event to capture keystrokes.
3. We update the React state with the keystroke.
4. The state updates, triggering a re-render, and React pushes the new string back down into the input's value prop.
React completely hijacks the input. The input only displays what React explicitly allows it to display.
Syntax
function SearchBar() {
// 1. Create the State (The Source of Truth)
const [query, setQuery] = useState("");
const handleTyping = (event) => {
// 3. When the user types, update the State
// (You can also intercept and modify the input here!)
setQuery(event.target.value.toUpperCase());
};
return (
<div>
{/* 2. Bind value to state, and onChange to the handler */}
<input
type="text"
value={query}
onChange={handleTyping}
/>
<p>Searching for: {query}</p>
</div>
);
}Common Pitfalls
- Missing the onChange handler: If you provide a
value={state}to an input but forget theonChangehandler, React locks the input down. The user will aggressively type on their keyboard, but the input will remain completely frozen on the screen, because the React state never changes to permit the new letters.
Interview Questions
In a Controlled component, the form data is handled strictly by React state (value and onChange). In an Uncontrolled component, the form data is handled by the browser DOM natively, and React only accesses the data when needed (e.g., on form submission) using a useRef attached to the input.
Because React is in full control, you can enforce formatting before the letter ever reaches the screen. For example, stripping out non-numeric characters for a phone number field, converting all text to uppercase, or instantly blocking a user from exceeding a character limit.
Real-World Example
Real-time Validation and formatting: This demonstrates the massive power of Controlled Inputs. If a user tries to type 'abcd', the replace function deletes the letters instantly. The state never updates, so the input ignores the letters entirely. This provides an incredibly robust, error-proof user experience.
function CreditCardInput() {
const [ccNumber, setCcNumber] = useState('');
const [error, setError] = useState('');
const handleChange = (e) => {
// 1. Strip all non-numeric characters
let rawStr = e.target.value.replace(/\D/g, '');
// 2. Prevent typing more than 16 digits
if (rawStr.length > 16) return;
// 3. Format with spaces every 4 digits (e.g., 1234 5678)
let formatted = rawStr.replace(/(\d{4})(?=\d)/g, '$1 ');
// 4. Update the state (which updates the UI)
setCcNumber(formatted);
// 5. Live validation
if (rawStr.length < 16) setError('Requires 16 digits');
else setError('');
};
return (
<div>
<input
value={ccNumber}
onChange={handleChange}
placeholder="0000 0000 0000 0000"
/>
{error && <span className="text-red-500">{error}</span>}
</div>
);
}Check Your Knowledge
Test your understanding of Controlled Inputs with these quick questions.