Controlled Components
Controlled components are the React pattern where form elements such as input, textarea, and select derive their current value from React state. User input updates that state, and the new state is rendered back into the form field.
▶Architecture Diagram
📊 Data FlowDashed line animations indicate the flow direction of data or requests
Browsers already keep their own internal form state. If React does not know the current input value, validation, conditional UI, and synchronization with the rest of the app become harder. Once the DOM and React disagree about the real current value, debugging form behavior gets messy.
Traditional form handling often waited until submit time and then read values from the DOM all at once. Modern frontend apps needed live validation, instant filtering, and relationships across fields while typing. That pressure made state-driven input control a widely used pattern.
A user types, the onChange handler reads the new value, and state updates. On the next render, that state is passed back into the field through value or checked. Because the field always reflects state, the form stays aligned with the rest of the component logic.
Binding field value and button state to the same source of truth
import { useState } from "react";
export function InviteForm() {
const [email, setEmail] = useState("");
return (
<>
<input
value={email}
onChange={(event) => setEmail(event.target.value)}
placeholder="name@example.com"
/>
<button disabled={email.trim() === ""}>Send invite</button>
</>
);
}Because the input and the button logic both read the same state value, React owns the current form state in one place.
Checkboxes also read checked from state
import { useState } from "react";
export function TermsField() {
const [agreed, setAgreed] = useState(false);
return (
<>
<label>
<input
type="checkbox"
checked={agreed}
onChange={(event) => setAgreed(event.target.checked)}
/>
I agree to the terms
</label>
<button disabled={!agreed}>Continue</button>
</>
);
}The pattern is the same for checkboxes: the browser does not stay the source of truth. React state does.
Controlled components are one form-specific application of state. In contrast, ref-based access keeps the DOM as the source of truth and reads the value when needed. Compared with general event handling, controlled components are the narrower pattern that specifically closes the loop between input events and form state.
Commonly Compared Concepts
State
The current value a component remembers across renders
Controlled components are a concrete way of applying state to form fields. State is the general mechanism; the controlled pattern is the form-specific usage.
Events
The way React connects user input to logic and state changes
Controlled components are built on input events, but they are more specific because they bind those events into a state loop for form values.
Ref
React's escape hatch for remembering mutable values and touching DOM nodes directly
Controlled inputs make state the source of truth, while ref-based access reads the DOM's current value directly when needed.
In practice, controlled inputs are especially strong for validation, debounced search, masked input, and UI that enables or disables actions from current field values. But very large forms can still suffer if all state is pushed too high, so field boundaries and ownership matter.