Every React developer encounters the challenge of managing user input. Some form fields need to be tamed with precise React state—others can be left to roam free in the DOM. That’s where the concepts of controlled and uncontrolled components come in.
Think of controlled components as disciplined performers on a script: their every move is dictated by React’s state. Uncontrolled components, on the other hand, are more like improvisers, letting the browser handle their behaviour until you check in on them with a ref.
This post unpacks how both styles work, why they matter, and how you can pick the right approach for every form in your app.
What are Controlled and Uncontrolled Components?
Controlled Component: A controlled component is an input element whose value is managed by React state. Whenever the input changes, React gets notified through an event like onChange, and you update the state accordingly.
Here, any change to the input updates React’s state, and React reflects that state back into the input.
Uncontrolled Component: An uncontrolled component keeps its own state in the DOM. Instead of relying on React state, you use a ref to get the value when needed.
Here, the input manages itself until you “reach in” and grab the value using a ref.
Controlled vs Uncontrolled: Key Differences
When Should You Use Each?
1- Use controlled components for:
- Real-time validation, formatting, or state updates.
- When you need to react to every change as it happens.
- Large, dynamic or multi-step forms.
2- Use uncontrolled components for:
- Quick, simple forms.
- Integrating with non-React libraries that manipulate the DOM.
- When you want performance benefits for very large forms.
Form Validation: Controlled vs Uncontrolled
Using React refs for Uncontrolled Components
defaultValue vs value: The Gotcha
Migrating from Uncontrolled to Controlled
Building Reusable Components (Support Both Modes)
Best Practices
- Always pick one mode: controlled or uncontrolled, and stick to it for a component’s lifecycle.
- Initialise controlled values with an empty string (useState("")) to avoid React warnings.
- Do not mix defaultValue and value on the same input.
- For reusable form components, document their usage clearly to prevent developer confusion.
