
Controlled vs. Uncontrolled Components in React

This Article is Over Ten Years Old...
Things can and do move very quickly in tech, which means that tech-related articles go out of date almost as soon as they have been written and published. If you are looking for up-to-date technical advice or opinion, it is unlikely that you will find it on this page.
You may find that my recent articles are more relevant, and you are always welcome to drop me a line if you have a specific technical problem you are trying to solve.
React forms are where many developers first discover that rendering HTML with a component model is not quite the same thing as just writing HTML.
An ordinary browser form can happily keep track of its own input values in the DOM. React, however, also gives us the option of treating those values as component state. That leads to the distinction between controlled and uncontrolled components.
The names sound slightly abstract, but the core difference is straightforward.
A controlled input gets its current value from React state.
An uncontrolled input keeps its own current value in the DOM, and React only reads it when needed.
Neither approach is automatically right in every situation. The useful question is what kind of control the interface actually needs.
A Controlled Input is Driven by React State
This is the pattern most React developers see first:
const SearchForm = (): JSX.Element => { const [query, setQuery] = useState(''); return ( <input value={query} onChange={(event) => setQuery(event.target.value)} /> );};The displayed value comes from query, and every keystroke updates that state through onChange.
That means React is the source of truth.
Why Controlled Inputs are so Popular
Once the value lives in state, React can do much more with it easily:
- validate as the user types
- show character counts
- enable or disable buttons
- conditionally render helper text
- transform the value before display
- keep several UI elements in sync
For example:
const SignupForm = (): JSX.Element => { const [email, setEmail] = useState(''); const isValid = email.includes('@'); return ( <> <input value={email} onChange={(event) => setEmail(event.target.value)} /> {!isValid && <p>Please enter a valid email address.</p>} <button disabled={!isValid}>Continue</button> </> );};This kind of immediate feedback is exactly where controlled components feel strong.
An Uncontrolled Input Keeps Its Value in the DOM
With an uncontrolled component, React does not keep the live value in state.
Instead, we usually read it through a ref when we need it:
const SearchForm = (): JSX.Element => { const inputRef = useRef<HTMLInputElement | null>(null); const handleSubmit = (event: FormEvent<HTMLFormElement>): void => { event.preventDefault(); console.log(inputRef.current?.value ?? ''); }; return ( <form onSubmit={handleSubmit}> <input ref={inputRef} defaultValue="" /> <button type="submit">Search</button> </form> );};Now the browser is managing the input's current value, not React state.
This is Closer to Traditional HTML Form Behaviour
That can be useful when:
- the input is simple
- you only need the value on submit
- live validation is unnecessary
- you are integrating with non‑React code
- you want less state bookkeeping
In other words, uncontrolled inputs are not some second‑rate fallback. Sometimes they are simply the lighter option.
value vs. defaultValue is the clue
This distinction matters.
For controlled inputs, we typically use:
value={stateValue}For uncontrolled inputs, we usually use:
defaultValue="initial text"defaultValue sets the initial value, but after that the DOM keeps control.
If you use value without updating it correctly, the input becomes effectively frozen because React keeps reapplying the same value.
Controlled Components Make State Explicit
That is their biggest strength.
The current form value is not hidden inside the browser. It is part of the component model, so the rest of the UI can respond to it deliberately.
This is especially useful for:
- complex forms
- conditional validation rules
- multi‑step flows
- filtering interfaces
- search and autocomplete
When the value affects other parts of the interface immediately, keeping it in React state is usually worth the extra ceremony.
Uncontrolled Components Reduce Overhead in Simpler Cases
Suppose we have one newsletter email field and only care about the value when the form is submitted.
Building a whole state‑update cycle for every keystroke may not buy us much. An uncontrolled input plus a ref can be perfectly reasonable.
The trick is not to treat every form as though it must be fully controlled by default.
File Inputs are the Famous Special Case
<input type="file" /> is typically treated as uncontrolled because the browser owns that value and React does not set it in the same way as normal text inputs.
That is a good reminder that the DOM still has its own rules. React gives us useful abstractions, but it does not erase the browser underneath.
A Common Mistake is Mixing the Two Approaches Carelessly
Developers often run into warnings when an input starts uncontrolled and later becomes controlled, or the other way around.
For example, an input might begin with undefined, then later receive a string value. React notices the mode changing and complains because the component's behaviour has become inconsistent.
The fix is usually to choose one model clearly from the start.
If it is controlled, initialise the value properly:
const [name, setName] = useState('');That way the input is controlled from the first render onwards.
The Question is Not "Which is More React"
Controlled components are often presented as the "proper" React approach. That is too simplistic.
The better question is:
How much influence does the rest of the UI need over this value while the user is editing it?
If the answer is "a lot", controlled components usually win.
If the answer is "hardly any", uncontrolled inputs may be a better fit.
Practical Guidance
Use controlled inputs when:
- the UI depends on the current value immediately
- validation happens during typing
- the value needs to stay in sync with other state
Use uncontrolled inputs when:
- the field is simple
- the value only matters at submit time
- you want to minimise form state code
Both Patterns Exist Because Forms are Not All the Same
That is the real lesson.
Some form interactions are dynamic, reactive, and closely tied to the rest of the component tree. Controlled components shine there.
Some are modest and self‑contained. Uncontrolled inputs handle those perfectly well without forcing the form into a heavier architecture than it needs.
Once you stop treating the distinction as a purity test, the choice becomes much easier. Controlled components give React more authority. Uncontrolled components leave more work with the browser. Good React code simply chooses the balance that fits the form in front of it.
Related Articles

Why this Changes in JavaScript Event Handlers and Methods. Why

React vs. Vue vs. Angular. React vs. Vue vs. Angular

Removing p Tags from Contentful List Items. Removing
pTags from Contentful List Items
Understanding CSS Transitions. Understanding CSS Transitions

Understanding Tail call Optimisation in JavaScript. Understanding Tail call Optimisation in JavaScript

Pass by Value vs. Reference in JavaScript. Pass by Value vs. Reference in JavaScript

Escaping and Unescaping Special Characters in JavaScript. Escaping and Unescaping Special Characters in JavaScript

Finding the Difference Between Two Strings in JavaScript. Finding the Difference Between Two Strings in JavaScript

Understanding the :hover Pseudo‑Class in CSS. Understanding the
:hoverPseudo‑Class in CSS
Fundamentals of HTML: A Guide. Fundamentals of HTML: A Guide

The arguments Object vs. Rest Parameters in JavaScript. The
argumentsObject vs. Rest Parameters in JavaScript
Closures in JavaScript: The Key to Lexical Scope. Closures in JavaScript: The Key to Lexical Scope