Controlled vs Uncontrolled Inputs in React

React forms confuse a lot of people, and it usually comes down to one distinction: is the input controlled or uncontrolled? Once that clicks, forms get simple.

Controlled inputs

A controlled input's value comes from React state. You set value and update state in onChange. React is the single source of truth:

function NameField() {
  const [name, setName] = useState('');
  return (
    <input
      value={name}
      onChange={e => setName(e.target.value)}
    />
  );
}

Because the value lives in state, you can validate as the user types, format input, disable a submit button, or reset the field — all with normal React code.

Uncontrolled inputs

An uncontrolled input keeps its own value in the DOM, like a plain HTML form. You read it when you need it, usually with a ref:

function NameField() {
  const inputRef = useRef(null);
  function handleSubmit() {
    console.log(inputRef.current.value);
  }
  return <input ref={inputRef} defaultValue="" />;
}

Note defaultValue instead of value — it sets the initial value without taking control of it.

Which should you use?

  • Controlled — the default choice. Use it when you need live validation, conditional UI, or to transform the value as it changes.
  • Uncontrolled — fine for simple forms where you only read values on submit, or when integrating non-React widgets. It's also handy for file inputs, which are always uncontrolled.

Key takeaways

  • Controlled = value in React state (value + onChange).
  • Uncontrolled = value in the DOM (defaultValue + a ref).
  • Reach for controlled inputs by default; uncontrolled for simple or non-React cases.

Want the structured path? Explore the React roadmap or browse more articles.