Controlled vs. Uncontrolled Components in React

Hero image for Controlled vs. Uncontrolled Components in React. Image by Avinash Kumar.
Hero image for 'Controlled vs. Uncontrolled Components in React.' Image by Avinash Kumar.

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.


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 nonReact code
  • you want less state bookkeeping

In other words, uncontrolled inputs are not some secondrate 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
  • multistep 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 stateupdate 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 selfcontained. 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.


Categories:

  1. Development
  2. Front‑End Development
  3. Guides
  4. JavaScript
  5. React